create-mendix-widget-gleam 2.0.18 → 2.0.21

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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/i18n.mjs +394 -334
  4. package/src/index.mjs +284 -231
  5. package/src/licenses.mjs +147 -0
  6. package/src/prompts.mjs +247 -142
  7. package/src/scaffold.mjs +108 -97
  8. package/src/templates/claude_md.mjs +3 -3
  9. package/src/templates/readme_md.mjs +10 -10
  10. package/template/gleam.toml +2 -2
  11. package/template/package.json +6 -6
  12. package/template/src/__WidgetName__.xml +1 -1
  13. package/template/src/package.xml +2 -2
  14. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@command.cache +0 -0
  15. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@cursor.cache +0 -0
  16. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@cursor.cache_meta +0 -0
  17. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@event.cache +0 -0
  18. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@internal@consts.cache +0 -0
  19. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@stdout.cache +0 -0
  20. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@style.cache +0 -0
  21. package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@terminal.cache +0 -0
  22. package/tui/build/dev/javascript/etch/etch/event.mjs +36 -30
  23. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@application.cache +0 -0
  24. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@atom.cache +0 -0
  25. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@charlist.cache +0 -0
  26. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@node.cache +0 -0
  27. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@port.cache +0 -0
  28. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@process.cache +0 -0
  29. package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@reference.cache +0 -0
  30. package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@array.cache +0 -0
  31. package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@promise.cache +0 -0
  32. package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@symbol.cache +0 -0
  33. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache +0 -0
  34. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache +0 -0
  35. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_inline +0 -0
  36. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache +0 -0
  37. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache +0 -0
  38. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache +0 -0
  39. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache +0 -0
  40. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta +0 -0
  41. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache +0 -0
  42. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache +0 -0
  43. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache +0 -0
  44. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache +0 -0
  45. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache +0 -0
  46. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache +0 -0
  47. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache +0 -0
  48. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache +0 -0
  49. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache +0 -0
  50. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_inline +0 -0
  51. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache +0 -0
  52. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache +0 -0
  53. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta +0 -0
  54. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache +0 -0
  55. package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache +0 -0
  56. package/tui/build/dev/javascript/gleam_version +1 -1
  57. package/tui/build/dev/javascript/prelude.mjs +10 -26
  58. package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache +0 -0
  59. package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache_meta +0 -0
  60. package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache +0 -0
  61. package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache_meta +0 -0
  62. package/tui/build/dev/javascript/tui/tui/prompt.mjs +713 -52
  63. package/tui/build/dev/javascript/tui/tui.mjs +438 -51
  64. package/tui/build/dev/javascript/tui/tui_ffi.mjs +12 -0
  65. package/template/LICENSE +0 -15
package/src/index.mjs CHANGED
@@ -1,231 +1,284 @@
1
- /**
2
- * create-mendix-widget-gleam main orchestration
3
- */
4
-
5
- import { resolve, dirname, join } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { mkdir, writeFile } from "node:fs/promises";
8
- import { execSync } from "node:child_process";
9
- import { collect_options } from "../tui/build/dev/javascript/tui/tui.mjs";
10
- import { collectOptions } from "./prompts.mjs";
11
- import { generateNames } from "./naming.mjs";
12
- import { getPmConfig } from "./pm.mjs";
13
- import { scaffold } from "./scaffold.mjs";
14
- import { t, getTemplateComments, getLangLabel } from "./i18n.mjs";
15
- import { generateClaudeMdContent } from "./templates/claude_md.mjs";
16
- import { generateReadmeContent } from "./templates/readme_md.mjs";
17
- import { generateWidgetsReadmeContent } from "./templates/widgets_readme.mjs";
18
-
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const TEMPLATE_DIR = resolve(__dirname, "..", "template");
21
-
22
- const BOLD = "\x1b[1m";
23
- const RESET = "\x1b[0m";
24
- const GREEN = "\x1b[32m";
25
- const CYAN = "\x1b[36m";
26
- const DIM = "\x1b[2m";
27
- const YELLOW = "\x1b[33m";
28
-
29
- const VERSION = "1.0.0";
30
-
31
- const HELP = `
32
- ${BOLD}create-mendix-widget-gleam${RESET} — Create Gleam + Mendix Pluggable Widget projects
33
-
34
- ${BOLD}Usage:${RESET}
35
- npx create-mendix-widget-gleam [project-name]
36
-
37
- ${BOLD}Options:${RESET}
38
- --help, -h Show help
39
- --version, -v Show version
40
-
41
- ${BOLD}Examples:${RESET}
42
- npx create-mendix-widget-gleam my-cool-widget
43
- npx create-mendix-widget-gleam MyCoolWidget
44
- `;
45
-
46
- export async function main(args) {
47
- // Flag handling
48
- if (args.includes("--help") || args.includes("-h")) {
49
- console.log(HELP);
50
- return;
51
- }
52
- if (args.includes("--version") || args.includes("-v")) {
53
- console.log(VERSION);
54
- return;
55
- }
56
-
57
- // Extract project name from CLI args (excluding flags)
58
- const positional = args.filter((a) => !a.startsWith("-"));
59
- const cliProjectName = positional[0] || "";
60
-
61
- // Collect options — etch TUI (TTY) with readline fallback (non-TTY/pipe)
62
- const header = `\n${BOLD}${CYAN}create-mendix-widget-gleam${RESET} ${DIM}v${VERSION}${RESET}\n`;
63
- let projectName, pm, lang;
64
- let usedFallback = false;
65
- try {
66
- const options = await collect_options(cliProjectName);
67
- projectName = options.project_name;
68
- pm = options.pm;
69
- lang = options.lang;
70
- } catch {
71
- // Fallback: readline prompts (non-TTY, CI, pipe, etc.)
72
- usedFallback = true;
73
- console.log(header);
74
- const result = await collectOptions(cliProjectName || null);
75
- projectName = result.projectName;
76
- pm = result.pm;
77
- lang = result.lang;
78
- }
79
-
80
- if (!usedFallback) console.log(header);
81
-
82
- // Name transformations
83
- const names = generateNames(projectName);
84
- if (!names) {
85
- console.error(`${YELLOW}${t(lang, "error.invalidName")}${RESET}`);
86
- process.exit(1);
87
- }
88
-
89
- const pmConfig = getPmConfig(pm);
90
- const targetDir = resolve(process.cwd(), names.kebabCase);
91
-
92
- // Summary
93
- console.log(`\n${BOLD}${t(lang, "summary.title")}${RESET}`);
94
- console.log(` ${t(lang, "summary.directory")} ${CYAN}${names.kebabCase}/${RESET}`);
95
- console.log(` ${t(lang, "summary.widgetName")} ${names.pascalCase}`);
96
- console.log(` ${t(lang, "summary.gleamModule")} ${names.snakeCase}`);
97
- console.log(` ${t(lang, "summary.packageManager")} ${pm}`);
98
- console.log(` ${t(lang, "summary.language")} ${getLangLabel(lang)}`);
99
- console.log();
100
-
101
- // Create directory
102
- await mkdir(targetDir, { recursive: true });
103
-
104
- // Build template comments (i18n for template placeholders)
105
- const templateComments = {
106
- ...getTemplateComments(lang),
107
- widgets_readme: generateWidgetsReadmeContent(lang),
108
- };
109
-
110
- // Scaffold templates
111
- console.log(`${DIM}${t(lang, "progress.generatingFiles")}${RESET}`);
112
- const created = await scaffold(TEMPLATE_DIR, targetDir, names, pmConfig, templateComments);
113
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.filesCreated", { count: created.length })}`);
114
-
115
- // Generate CLAUDE.md (always English, comment lang instruction varies)
116
- await writeFile(
117
- join(targetDir, "CLAUDE.md"),
118
- generateClaudeMdContent(lang, names, pm, pmConfig),
119
- "utf-8",
120
- );
121
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.claudeMdCreated")}`);
122
-
123
- // Generate README.md
124
- await writeFile(
125
- join(targetDir, "README.md"),
126
- generateReadmeContent(lang, names, pm, pmConfig),
127
- "utf-8",
128
- );
129
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.readmeCreated")}`);
130
-
131
- // git init
132
- try {
133
- execSync("git init", { cwd: targetDir, stdio: "ignore" });
134
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.gitInit")}`);
135
- } catch {
136
- // git not available — continue
137
- }
138
-
139
- // Gleam → JS compilation (pipe로 Erlang 관련 Unused value warning 숨김)
140
- console.log(`\n${BOLD}${t(lang, "progress.gleamCompiling")}${RESET}`);
141
- try {
142
- execSync("gleam build --target javascript", {
143
- cwd: targetDir,
144
- stdio: "pipe",
145
- });
146
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.gleamCompiled")}`);
147
- } catch (e) {
148
- const output = (e.stderr || e.stdout || "").toString();
149
- if (output) console.error(output);
150
- console.error(
151
- `\n${YELLOW}${t(lang, "error.gleamCompileFail")}${RESET}`,
152
- );
153
- console.error(` ${CYAN}gleam build --target javascript${RESET}\n`);
154
- }
155
-
156
- // Install dependencies
157
- console.log(`\n${BOLD}${t(lang, "progress.depsInstalling", { pm })}${RESET}\n`);
158
- try {
159
- execSync(pmConfig.install, {
160
- cwd: targetDir,
161
- stdio: "inherit",
162
- });
163
- console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.depsInstalled")}`);
164
- } catch {
165
- console.error(
166
- `\n${YELLOW}${t(lang, "error.depsInstallFail")}${RESET}`,
167
- );
168
- console.error(` ${CYAN}${pmConfig.install}${RESET}\n`);
169
- }
170
-
171
- // Install Playwright Chromium (only if not already installed)
172
- try {
173
- const chromiumExists =
174
- execSync(
175
- `node -e "const fs=require('fs'),pw=require('playwright');process.stdout.write(String(fs.existsSync(pw.chromium.executablePath())))"`,
176
- { cwd: targetDir, encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] },
177
- ).trim() === "true";
178
-
179
- if (!chromiumExists) {
180
- console.log(`\n${BOLD}${t(lang, "progress.playwrightInstalling")}${RESET}\n`);
181
- try {
182
- execSync("npx playwright install chromium", {
183
- cwd: targetDir,
184
- stdio: "inherit",
185
- });
186
- console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.playwrightInstalled")}`);
187
- } catch {
188
- console.error(
189
- `\n${YELLOW}${t(lang, "error.playwrightFail")}${RESET}`,
190
- );
191
- console.error(
192
- ` ${CYAN}npx playwright install chromium${RESET}\n`,
193
- );
194
- }
195
- } else {
196
- console.log(`${GREEN}✓${RESET} ${t(lang, "progress.playwrightExists")}`);
197
- }
198
- } catch {
199
- // playwright package not installed — ignore
200
- }
201
-
202
- // Production build
203
- console.log(`\n${BOLD}${t(lang, "progress.buildingWidget")}${RESET}\n`);
204
- try {
205
- execSync("gleam run -m glendix/build", {
206
- cwd: targetDir,
207
- stdio: "inherit",
208
- });
209
- console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.widgetBuilt")}`);
210
- } catch {
211
- console.error(
212
- `\n${YELLOW}${t(lang, "error.buildFail")}${RESET}`,
213
- );
214
- console.error(` ${CYAN}gleam run -m glendix/build${RESET}\n`);
215
- }
216
-
217
- // Done
218
- console.log(`
219
- ${GREEN}${BOLD}${t(lang, "done.title")}${RESET}
220
-
221
- ${BOLD}${t(lang, "done.nextSteps")}${RESET}
222
-
223
- ${CYAN}cd ${names.kebabCase}${RESET}
224
- ${CYAN}gleam run -m glendix/dev${RESET} ${DIM}${t(lang, "done.devServer")}${RESET}
225
- ${CYAN}gleam run -m glendix/build${RESET} ${DIM}${t(lang, "done.prodBuild")}${RESET}
226
- ${CYAN}gleam run -m glendix/marketplace${RESET} ${DIM}${t(lang, "done.marketplace")}${RESET}
227
- `);
228
-
229
- // etch TUI 이벤트 서버의 stdin 리스너가 이벤트 루프를 유지하므로 명시적 종료
230
- process.exit(0);
231
- }
1
+ /**
2
+ * create-mendix-widget-gleam main orchestration
3
+ */
4
+
5
+ import { resolve, dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { mkdir, writeFile } from "node:fs/promises";
8
+ import { execSync } from "node:child_process";
9
+ import { collect_options } from "../tui/build/dev/javascript/tui/tui.mjs";
10
+ import { collectOptions } from "./prompts.mjs";
11
+ import { generateNames } from "./naming.mjs";
12
+ import { getPmConfig } from "./pm.mjs";
13
+ import { scaffold } from "./scaffold.mjs";
14
+ import { t, getTemplateComments, getLangLabel } from "./i18n.mjs";
15
+ import { generateClaudeMdContent } from "./templates/claude_md.mjs";
16
+ import { generateReadmeContent } from "./templates/readme_md.mjs";
17
+ import { generateWidgetsReadmeContent } from "./templates/widgets_readme.mjs";
18
+ import { generateLicenseContent } from "./licenses.mjs";
19
+
20
+ const __dirname = dirname(fileURLToPath(import.meta.url));
21
+ const TEMPLATE_DIR = resolve(__dirname, "..", "template");
22
+
23
+ const BOLD = "\x1b[1m";
24
+ const RESET = "\x1b[0m";
25
+ const GREEN = "\x1b[32m";
26
+ const CYAN = "\x1b[36m";
27
+ const DIM = "\x1b[2m";
28
+ const YELLOW = "\x1b[33m";
29
+ const MAGENTA = "\x1b[35m";
30
+
31
+ const VERSION = "1.0.0";
32
+
33
+ const HELP = `
34
+ ${BOLD}create-mendix-widget-gleam${RESET} — Create Gleam + Mendix Pluggable Widget projects
35
+
36
+ ${BOLD}Usage:${RESET}
37
+ npx create-mendix-widget-gleam [project-name]
38
+
39
+ ${BOLD}Options:${RESET}
40
+ --help, -h Show help
41
+ --version, -v Show version
42
+
43
+ ${BOLD}Examples:${RESET}
44
+ npx create-mendix-widget-gleam my-cool-widget
45
+ npx create-mendix-widget-gleam MyCoolWidget
46
+ `;
47
+
48
+ const BANNER_LINES = [
49
+ [" ████ ████ ████", ""],
50
+ [" ████ ████ ██████", ""],
51
+ [" ████ ████ ████", ""],
52
+ [" ████ ████", " █▓█"],
53
+ [" ████████ ████ ████ ████████ ████ ███████ ███████ ████ ████", " ▓░▓█"],
54
+ [" ███████████████ ████ ████████████ ██████████████ ██████████████ ████", " ▓░░▓▓▓▓"],
55
+ ["█████ ██████ ████ ████ █████ █████ ████ █████ █████ ████", " ▓▓░░░░░░▓"],
56
+ ["████ █████ ████ ████ ████ █████ ████ ████ █████ ████", " ▓░░░░░▒░▒█"],
57
+ ["████ ████ ████ ███████████████ █████ ████ ████ ████ ████", " █▓░▒░▒░░▓"],
58
+ ["█████ █████ ████ ████ █████ ████ ████ █████ ████", " █▓░░▒░░▓"],
59
+ [" ███████████████ ████ █████ ██ █████ ████ █████ ██████ ████", " ▓░░░░░▓"],
60
+ [" █████████ ████ ████ ████████████ █████ ████ ██████████████ ████", " ▓░▒▓█▓▓"],
61
+ [" ████ ████ ████████ █████ ████ ██████ ████ ████", " █▓█"],
62
+ [" █ ████", ""],
63
+ [" █████████████", ""],
64
+ [" ██████████", ""],
65
+ ];
66
+
67
+ const header = '\n' + BANNER_LINES.map(([g, l]) =>
68
+ `${CYAN}${BOLD}${g}${RESET}${MAGENTA}${l}${RESET}`
69
+ ).join('\n') + `\n${DIM} create-mendix-widget-gleam v${VERSION}${RESET}\n`;
70
+
71
+ export async function main(args) {
72
+ // Flag handling
73
+ if (args.includes("--help") || args.includes("-h")) {
74
+ console.log(HELP);
75
+ return;
76
+ }
77
+ if (args.includes("--version") || args.includes("-v")) {
78
+ console.log(VERSION);
79
+ return;
80
+ }
81
+
82
+ // Extract project name from CLI args (excluding flags)
83
+ const positional = args.filter((a) => !a.startsWith("-"));
84
+ const cliProjectName = positional[0] || "";
85
+
86
+ // Collect options — etch TUI (TTY) with readline fallback (non-TTY/pipe)
87
+ let projectName, pm, lang, organization, copyright, license, version, author, projectPath;
88
+ let usedFallback = false;
89
+ try {
90
+ const options = await collect_options(cliProjectName);
91
+ projectName = options.project_name;
92
+ pm = options.pm;
93
+ lang = options.lang;
94
+ organization = options.organization;
95
+ copyright = options.copyright;
96
+ license = options.license;
97
+ version = options.version;
98
+ author = options.author;
99
+ projectPath = options.project_path;
100
+ } catch {
101
+ // Fallback: readline prompts (non-TTY, CI, pipe, etc.)
102
+ usedFallback = true;
103
+ console.log(header);
104
+ const result = await collectOptions(cliProjectName || null);
105
+ projectName = result.projectName;
106
+ pm = result.pm;
107
+ lang = result.lang;
108
+ organization = result.organization;
109
+ copyright = result.copyright;
110
+ license = result.license;
111
+ version = result.version;
112
+ author = result.author;
113
+ projectPath = result.projectPath;
114
+ }
115
+
116
+ if (!usedFallback) console.log(header);
117
+
118
+ // Name transformations
119
+ const names = generateNames(projectName);
120
+ if (!names) {
121
+ console.error(`${YELLOW}${t(lang, "error.invalidName")}${RESET}`);
122
+ process.exit(1);
123
+ }
124
+
125
+ const pmConfig = getPmConfig(pm);
126
+ const targetDir = resolve(process.cwd(), names.kebabCase);
127
+
128
+ // Summary
129
+ console.log(`\n${BOLD}${t(lang, "summary.title")}${RESET}`);
130
+ console.log(` ${t(lang, "summary.directory")} ${CYAN}${names.kebabCase}/${RESET}`);
131
+ console.log(` ${t(lang, "summary.widgetName")} ${names.pascalCase}`);
132
+ console.log(` ${t(lang, "summary.gleamModule")} ${names.snakeCase}`);
133
+ console.log(` ${t(lang, "summary.organization")} ${organization}`);
134
+ console.log(` ${t(lang, "summary.version")} ${version}`);
135
+ console.log(` ${t(lang, "summary.license")} ${license}`);
136
+ console.log(` ${t(lang, "summary.author")} ${author}`);
137
+ console.log(` ${t(lang, "summary.copyright")} ${copyright}`);
138
+ console.log(` ${t(lang, "summary.projectPath")} ${projectPath}`);
139
+ console.log(` ${t(lang, "summary.packageManager")} ${pm}`);
140
+ console.log(` ${t(lang, "summary.language")} ${getLangLabel(lang)}`);
141
+ console.log();
142
+
143
+ // Create directory
144
+ await mkdir(targetDir, { recursive: true });
145
+
146
+ // Build template comments (i18n for template placeholders)
147
+ const templateComments = {
148
+ ...getTemplateComments(lang),
149
+ widgets_readme: generateWidgetsReadmeContent(lang),
150
+ };
151
+
152
+ // Scaffold options
153
+ const scaffoldOptions = { organization, copyright, license, version, author, projectPath };
154
+
155
+ // Scaffold templates
156
+ console.log(`${DIM}${t(lang, "progress.generatingFiles")}${RESET}`);
157
+ const created = await scaffold(TEMPLATE_DIR, targetDir, names, pmConfig, templateComments, scaffoldOptions);
158
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.filesCreated", { count: created.length })}`);
159
+
160
+ // Generate LICENSE
161
+ await writeFile(
162
+ join(targetDir, "LICENSE"),
163
+ generateLicenseContent(license, copyright),
164
+ "utf-8",
165
+ );
166
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.licenseCreated")}`);
167
+
168
+ // Generate CLAUDE.md (always English, comment lang instruction varies)
169
+ await writeFile(
170
+ join(targetDir, "CLAUDE.md"),
171
+ generateClaudeMdContent(lang, names, pm, pmConfig, organization),
172
+ "utf-8",
173
+ );
174
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.claudeMdCreated")}`);
175
+
176
+ // Generate README.md
177
+ await writeFile(
178
+ join(targetDir, "README.md"),
179
+ generateReadmeContent(lang, names, pm, pmConfig, license),
180
+ "utf-8",
181
+ );
182
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.readmeCreated")}`);
183
+
184
+ // git init
185
+ try {
186
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
187
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.gitInit")}`);
188
+ } catch {
189
+ // git not available — continue
190
+ }
191
+
192
+ // Gleam JS compilation (pipe로 Erlang 관련 Unused value warning 숨김)
193
+ console.log(`\n${BOLD}${t(lang, "progress.gleamCompiling")}${RESET}`);
194
+ try {
195
+ execSync("gleam build --target javascript", {
196
+ cwd: targetDir,
197
+ stdio: "pipe",
198
+ });
199
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.gleamCompiled")}`);
200
+ } catch (e) {
201
+ const output = (e.stderr || e.stdout || "").toString();
202
+ if (output) console.error(output);
203
+ console.error(
204
+ `\n${YELLOW}${t(lang, "error.gleamCompileFail")}${RESET}`,
205
+ );
206
+ console.error(` ${CYAN}gleam build --target javascript${RESET}\n`);
207
+ }
208
+
209
+ // Install dependencies
210
+ console.log(`\n${BOLD}${t(lang, "progress.depsInstalling", { pm })}${RESET}\n`);
211
+ try {
212
+ execSync(pmConfig.install, {
213
+ cwd: targetDir,
214
+ stdio: "inherit",
215
+ });
216
+ console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.depsInstalled")}`);
217
+ } catch {
218
+ console.error(
219
+ `\n${YELLOW}${t(lang, "error.depsInstallFail")}${RESET}`,
220
+ );
221
+ console.error(` ${CYAN}${pmConfig.install}${RESET}\n`);
222
+ }
223
+
224
+ // Install Playwright Chromium (only if not already installed)
225
+ try {
226
+ const chromiumExists =
227
+ execSync(
228
+ `node -e "const fs=require('fs'),pw=require('playwright');process.stdout.write(String(fs.existsSync(pw.chromium.executablePath())))"`,
229
+ { cwd: targetDir, encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] },
230
+ ).trim() === "true";
231
+
232
+ if (!chromiumExists) {
233
+ console.log(`\n${BOLD}${t(lang, "progress.playwrightInstalling")}${RESET}\n`);
234
+ try {
235
+ execSync("npx playwright install chromium", {
236
+ cwd: targetDir,
237
+ stdio: "inherit",
238
+ });
239
+ console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.playwrightInstalled")}`);
240
+ } catch {
241
+ console.error(
242
+ `\n${YELLOW}${t(lang, "error.playwrightFail")}${RESET}`,
243
+ );
244
+ console.error(
245
+ ` ${CYAN}npx playwright install chromium${RESET}\n`,
246
+ );
247
+ }
248
+ } else {
249
+ console.log(`${GREEN}✓${RESET} ${t(lang, "progress.playwrightExists")}`);
250
+ }
251
+ } catch {
252
+ // playwright package not installed — ignore
253
+ }
254
+
255
+ // Production build
256
+ console.log(`\n${BOLD}${t(lang, "progress.buildingWidget")}${RESET}\n`);
257
+ try {
258
+ execSync("gleam run -m glendix/build", {
259
+ cwd: targetDir,
260
+ stdio: "inherit",
261
+ });
262
+ console.log(`\n${GREEN}✓${RESET} ${t(lang, "progress.widgetBuilt")}`);
263
+ } catch {
264
+ console.error(
265
+ `\n${YELLOW}${t(lang, "error.buildFail")}${RESET}`,
266
+ );
267
+ console.error(` ${CYAN}gleam run -m glendix/build${RESET}\n`);
268
+ }
269
+
270
+ // Done
271
+ console.log(`
272
+ ${GREEN}${BOLD}${t(lang, "done.title")}${RESET}
273
+
274
+ ${BOLD}${t(lang, "done.nextSteps")}${RESET}
275
+
276
+ ${CYAN}cd ${names.kebabCase}${RESET}
277
+ ${CYAN}gleam run -m glendix/dev${RESET} ${DIM}${t(lang, "done.devServer")}${RESET}
278
+ ${CYAN}gleam run -m glendix/build${RESET} ${DIM}${t(lang, "done.prodBuild")}${RESET}
279
+ ${CYAN}gleam run -m glendix/marketplace${RESET} ${DIM}${t(lang, "done.marketplace")}${RESET}
280
+ `);
281
+
282
+ // etch TUI 이벤트 서버의 stdin 리스너가 이벤트 루프를 유지하므로 명시적 종료
283
+ process.exit(0);
284
+ }