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
@@ -0,0 +1,147 @@
1
+ /**
2
+ * License file content generators
3
+ */
4
+
5
+ export function generateLicenseContent(licenseId, copyright) {
6
+ switch (licenseId) {
7
+ case "Apache-2.0":
8
+ return apacheLicense(copyright);
9
+ case "MIT":
10
+ return mitLicense(copyright);
11
+ case "GPL-3.0-only":
12
+ return gpl3License(copyright);
13
+ case "GPL-2.0-only":
14
+ return gpl2License(copyright);
15
+ case "MPL-2.0":
16
+ return mplLicense(copyright);
17
+ case "BlueOak-1.0.0":
18
+ return blueOakLicense(copyright);
19
+ default:
20
+ return apacheLicense(copyright);
21
+ }
22
+ }
23
+
24
+ function apacheLicense(copyright) {
25
+ return `The Apache License v2.0
26
+
27
+ Copyright ${copyright}
28
+
29
+ Licensed under the Apache License, Version 2.0 (the "License");
30
+ you may not use this file except in compliance with the License.
31
+ You may obtain a copy of the License at
32
+
33
+ http://www.apache.org/licenses/LICENSE-2.0
34
+
35
+ Unless required by applicable law or agreed to in writing, software
36
+ distributed under the License is distributed on an "AS IS" BASIS,
37
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38
+ See the License for the specific language governing permissions and
39
+ limitations under the License.
40
+ `;
41
+ }
42
+
43
+ function mitLicense(copyright) {
44
+ return `MIT License
45
+
46
+ Copyright (c) ${copyright}
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining a copy
49
+ of this software and associated documentation files (the "Software"), to deal
50
+ in the Software without restriction, including without limitation the rights
51
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52
+ copies of the Software, and to permit persons to whom the Software is
53
+ furnished to do so, subject to the following conditions:
54
+
55
+ The above copyright notice and this permission notice shall be included in all
56
+ copies or substantial portions of the Software.
57
+
58
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
64
+ SOFTWARE.
65
+ `;
66
+ }
67
+
68
+ function gpl3License(copyright) {
69
+ return `GNU GENERAL PUBLIC LICENSE
70
+ Version 3, 29 June 2007
71
+
72
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
73
+
74
+ Everyone is permitted to copy and distribute verbatim copies of this
75
+ license document, but changing it is not allowed.
76
+
77
+ Copyright (C) ${copyright}
78
+
79
+ This program is free software: you can redistribute it and/or modify
80
+ it under the terms of the GNU General Public License as published by
81
+ the Free Software Foundation, version 3 of the License.
82
+
83
+ This program is distributed in the hope that it will be useful,
84
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
85
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86
+ GNU General Public License for more details.
87
+
88
+ You should have received a copy of the GNU General Public License
89
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
90
+ `;
91
+ }
92
+
93
+ function gpl2License(copyright) {
94
+ return `GNU GENERAL PUBLIC LICENSE
95
+ Version 2, June 1991
96
+
97
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
98
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
99
+
100
+ Everyone is permitted to copy and distribute verbatim copies of this
101
+ license document, but changing it is not allowed.
102
+
103
+ Copyright (C) ${copyright}
104
+
105
+ This program is free software; you can redistribute it and/or modify
106
+ it under the terms of the GNU General Public License as published by
107
+ the Free Software Foundation; version 2 of the License.
108
+
109
+ This program is distributed in the hope that it will be useful,
110
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
111
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
112
+ GNU General Public License for more details.
113
+
114
+ You should have received a copy of the GNU General Public License
115
+ along with this program; if not, write to the Free Software Foundation,
116
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
117
+ `;
118
+ }
119
+
120
+ function mplLicense(copyright) {
121
+ return `Mozilla Public License Version 2.0
122
+
123
+ Copyright ${copyright}
124
+
125
+ This Source Code Form is subject to the terms of the Mozilla Public
126
+ License, v. 2.0. If a copy of the MPL was not distributed with this
127
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
128
+ `;
129
+ }
130
+
131
+ function blueOakLicense(copyright) {
132
+ return `Blue Oak Model License 1.0.0
133
+
134
+ Copyright ${copyright}
135
+
136
+ Permission to use, copy, modify, and/or distribute this software for
137
+ any purpose with or without fee is hereby granted.
138
+
139
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
140
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
141
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
142
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
145
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
146
+ `;
147
+ }
package/src/prompts.mjs CHANGED
@@ -1,142 +1,247 @@
1
- /**
2
- * Interactive prompts (node:readline based)
3
- */
4
-
5
- import { createInterface } from "node:readline/promises";
6
- import { stdin, stdout } from "node:process";
7
- import { existsSync } from "node:fs";
8
- import { resolve } from "node:path";
9
- import { splitWords } from "./naming.mjs";
10
- import { PM_CHOICES, detectPm } from "./pm.mjs";
11
- import { LANG_CHOICES, getLangLabel, t } from "./i18n.mjs";
12
-
13
- const BOLD = "\x1b[1m";
14
- const DIM = "\x1b[2m";
15
- const RESET = "\x1b[0m";
16
- const CYAN = "\x1b[36m";
17
- const YELLOW = "\x1b[33m";
18
-
19
- /** Project name validation */
20
- function validateName(lang, name) {
21
- if (!name || name.trim().length === 0) {
22
- return t(lang, "validate.nameRequired");
23
- }
24
- const words = splitWords(name.trim());
25
- if (words.length === 0) {
26
- return t(lang, "validate.needAlpha");
27
- }
28
- if (!/^[a-zA-Z][a-zA-Z0-9\-_]*$/.test(name.trim())) {
29
- return t(lang, "validate.invalidChars");
30
- }
31
- return null;
32
- }
33
-
34
- /** Collect options via interactive prompts */
35
- export async function collectOptions(cliProjectName) {
36
- const rl = createInterface({ input: stdin, output: stdout });
37
- let done = false;
38
-
39
- rl.on("close", () => {
40
- if (!done) {
41
- console.log("\nCancelled.");
42
- process.exit(0);
43
- }
44
- });
45
-
46
- let projectName = cliProjectName;
47
-
48
- try {
49
- // 1. Language selection (multilingual labels — shown before language is chosen)
50
- console.log(
51
- `\n${BOLD}Language / 언어 / 言語:${RESET}`,
52
- );
53
- LANG_CHOICES.forEach((code, i) => {
54
- const label = getLangLabel(code);
55
- console.log(` ${i + 1}) ${label}`);
56
- });
57
-
58
- let langAnswer = "";
59
- try {
60
- langAnswer = await rl.question(
61
- `${DIM}(1-${LANG_CHOICES.length}, default: 1)${RESET}: `,
62
- );
63
- } catch {
64
- // stdin closed — use default
65
- }
66
-
67
- let lang = "en";
68
- const langIndex = parseInt(langAnswer, 10);
69
- if (langIndex >= 1 && langIndex <= LANG_CHOICES.length) {
70
- lang = LANG_CHOICES[langIndex - 1];
71
- }
72
-
73
- // 2. Project name
74
- if (!projectName) {
75
- projectName = await rl.question(
76
- `${BOLD}${t(lang, "prompt.projectName")}${RESET} `,
77
- );
78
- }
79
-
80
- const nameError = validateName(lang, projectName);
81
- if (nameError) {
82
- done = true;
83
- rl.close();
84
- console.error(`\n${YELLOW}${nameError}${RESET}`);
85
- process.exit(1);
86
- }
87
-
88
- projectName = projectName.trim();
89
-
90
- // Check directory conflict
91
- const targetDir = resolve(process.cwd(), projectName);
92
- if (existsSync(targetDir)) {
93
- done = true;
94
- rl.close();
95
- console.error(
96
- `\n${YELLOW}${t(lang, "error.dirExists", { name: projectName })}${RESET}`,
97
- );
98
- process.exit(1);
99
- }
100
-
101
- // 3. Package manager selection
102
- const detected = detectPm();
103
- console.log(
104
- `\n${BOLD}${t(lang, "prompt.pmSelect")}${RESET} ${DIM}(${t(lang, "prompt.pmDetected", { detected })})${RESET}`,
105
- );
106
- PM_CHOICES.forEach((pm, i) => {
107
- const marker =
108
- pm === detected
109
- ? ` ${CYAN}${t(lang, "prompt.pmDetectedMarker")}${RESET}`
110
- : "";
111
- console.log(` ${i + 1}) ${pm}${marker}`);
112
- });
113
-
114
- let pmAnswer = "";
115
- try {
116
- pmAnswer = await rl.question(
117
- `${t(lang, "prompt.pmChoose", { count: PM_CHOICES.length, default: detected })}: `,
118
- );
119
- } catch {
120
- // stdin closed use default
121
- }
122
-
123
- let pm = detected;
124
- const pmIndex = parseInt(pmAnswer, 10);
125
- if (pmIndex >= 1 && pmIndex <= PM_CHOICES.length) {
126
- pm = PM_CHOICES[pmIndex - 1];
127
- } else if (pmAnswer.trim() && PM_CHOICES.includes(pmAnswer.trim())) {
128
- pm = pmAnswer.trim();
129
- }
130
-
131
- done = true;
132
- rl.close();
133
- return { projectName, pm, lang };
134
- } catch (err) {
135
- done = true;
136
- rl.close();
137
- if (err.code === "ERR_USE_AFTER_CLOSE") {
138
- process.exit(0);
139
- }
140
- throw err;
141
- }
142
- }
1
+ /**
2
+ * Interactive prompts (node:readline based)
3
+ */
4
+
5
+ import { createInterface } from "node:readline/promises";
6
+ import { stdin, stdout } from "node:process";
7
+ import { existsSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ import { splitWords } from "./naming.mjs";
10
+ import { PM_CHOICES, detectPm } from "./pm.mjs";
11
+ import { LANG_CHOICES, getLangLabel, t } from "./i18n.mjs";
12
+
13
+ const BOLD = "\x1b[1m";
14
+ const DIM = "\x1b[2m";
15
+ const RESET = "\x1b[0m";
16
+ const CYAN = "\x1b[36m";
17
+ const YELLOW = "\x1b[33m";
18
+
19
+ const LICENSE_CHOICES = [
20
+ "Apache-2.0",
21
+ "BlueOak-1.0.0",
22
+ "GPL-3.0-only",
23
+ "GPL-2.0-only",
24
+ "MIT",
25
+ "MPL-2.0",
26
+ ];
27
+
28
+ /** Project name validation */
29
+ function validateName(lang, name) {
30
+ if (!name || name.trim().length === 0) {
31
+ return t(lang, "validate.nameRequired");
32
+ }
33
+ const words = splitWords(name.trim());
34
+ if (words.length === 0) {
35
+ return t(lang, "validate.needAlpha");
36
+ }
37
+ if (!/^[a-zA-Z][a-zA-Z0-9\-_]*$/.test(name.trim())) {
38
+ return t(lang, "validate.invalidChars");
39
+ }
40
+ return null;
41
+ }
42
+
43
+ /** Collect options via interactive prompts */
44
+ export async function collectOptions(cliProjectName) {
45
+ const rl = createInterface({ input: stdin, output: stdout });
46
+ let done = false;
47
+
48
+ rl.on("close", () => {
49
+ if (!done) {
50
+ console.log("\nCancelled.");
51
+ process.exit(0);
52
+ }
53
+ });
54
+
55
+ let projectName = cliProjectName;
56
+
57
+ try {
58
+ // 1. Language selection (multilingual labels — shown before language is chosen)
59
+ console.log(
60
+ `\n${BOLD}Language / 언어 / 言語:${RESET}`,
61
+ );
62
+ LANG_CHOICES.forEach((code, i) => {
63
+ const label = getLangLabel(code);
64
+ console.log(` ${i + 1}) ${label}`);
65
+ });
66
+
67
+ let langAnswer = "";
68
+ try {
69
+ langAnswer = await rl.question(
70
+ `${DIM}(1-${LANG_CHOICES.length}, default: 1)${RESET}: `,
71
+ );
72
+ } catch {
73
+ // stdin closed — use default
74
+ }
75
+
76
+ let lang = "en";
77
+ const langIndex = parseInt(langAnswer, 10);
78
+ if (langIndex >= 1 && langIndex <= LANG_CHOICES.length) {
79
+ lang = LANG_CHOICES[langIndex - 1];
80
+ }
81
+
82
+ // 2. Project name
83
+ if (!projectName) {
84
+ projectName = await rl.question(
85
+ `${BOLD}${t(lang, "prompt.projectName")}${RESET} `,
86
+ );
87
+ }
88
+
89
+ const nameError = validateName(lang, projectName);
90
+ if (nameError) {
91
+ done = true;
92
+ rl.close();
93
+ console.error(`\n${YELLOW}${nameError}${RESET}`);
94
+ process.exit(1);
95
+ }
96
+
97
+ projectName = projectName.trim();
98
+
99
+ // Check directory conflict
100
+ const targetDir = resolve(process.cwd(), projectName);
101
+ if (existsSync(targetDir)) {
102
+ done = true;
103
+ rl.close();
104
+ console.error(
105
+ `\n${YELLOW}${t(lang, "error.dirExists", { name: projectName })}${RESET}`,
106
+ );
107
+ process.exit(1);
108
+ }
109
+
110
+ // 3. Organization
111
+ const defaultOrg = "mendix";
112
+ let orgAnswer = "";
113
+ try {
114
+ orgAnswer = await rl.question(
115
+ `\n${BOLD}${t(lang, "prompt.orgName")}${RESET} ${DIM}[${defaultOrg}]${RESET}: `,
116
+ );
117
+ } catch {
118
+ // stdin closed — use default
119
+ }
120
+ const organization = orgAnswer.trim() || defaultOrg;
121
+
122
+ if (!/^[a-z][a-z0-9\-]*$/.test(organization)) {
123
+ done = true;
124
+ rl.close();
125
+ console.error(`\n${YELLOW}${t(lang, "validate.orgInvalid")}${RESET}`);
126
+ process.exit(1);
127
+ }
128
+
129
+ // 4. Copyright
130
+ const year = new Date().getFullYear();
131
+ const defaultCopyright = `© Mendix Technology BV ${year}. All rights reserved.`;
132
+ let copyrightAnswer = "";
133
+ try {
134
+ copyrightAnswer = await rl.question(
135
+ `\n${BOLD}${t(lang, "prompt.copyright")}${RESET} ${DIM}[${defaultCopyright}]${RESET}:\n `,
136
+ );
137
+ } catch {
138
+ // stdin closed — use default
139
+ }
140
+ const copyright = copyrightAnswer.trim() || defaultCopyright;
141
+
142
+ // 5. License selection
143
+ console.log(`\n${BOLD}${t(lang, "prompt.licenseSelect")}${RESET}`);
144
+ LICENSE_CHOICES.forEach((lic, i) => {
145
+ console.log(` ${i + 1}) ${lic}`);
146
+ });
147
+
148
+ let licAnswer = "";
149
+ try {
150
+ licAnswer = await rl.question(
151
+ `${DIM}(1-${LICENSE_CHOICES.length}, default: 1)${RESET}: `,
152
+ );
153
+ } catch {
154
+ // stdin closed — use default
155
+ }
156
+
157
+ let license = "Apache-2.0";
158
+ const licIndex = parseInt(licAnswer, 10);
159
+ if (licIndex >= 1 && licIndex <= LICENSE_CHOICES.length) {
160
+ license = LICENSE_CHOICES[licIndex - 1];
161
+ }
162
+
163
+ // 6. Version
164
+ const defaultVersion = "0.0.1";
165
+ let versionAnswer = "";
166
+ try {
167
+ versionAnswer = await rl.question(
168
+ `\n${BOLD}${t(lang, "prompt.version")}${RESET} ${DIM}[${defaultVersion}]${RESET}: `,
169
+ );
170
+ } catch {
171
+ // stdin closed — use default
172
+ }
173
+ const version = versionAnswer.trim() || defaultVersion;
174
+
175
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
176
+ done = true;
177
+ rl.close();
178
+ console.error(`\n${YELLOW}${t(lang, "validate.versionInvalid")}${RESET}`);
179
+ process.exit(1);
180
+ }
181
+
182
+ // 7. Author
183
+ const defaultAuthor = "A.N. Other";
184
+ let authorAnswer = "";
185
+ try {
186
+ authorAnswer = await rl.question(
187
+ `\n${BOLD}${t(lang, "prompt.author")}${RESET} ${DIM}[${defaultAuthor}]${RESET}: `,
188
+ );
189
+ } catch {
190
+ // stdin closed — use default
191
+ }
192
+ const author = authorAnswer.trim() || defaultAuthor;
193
+
194
+ // 8. Project Path
195
+ const defaultPath = "./tests/testProject";
196
+ let pathAnswer = "";
197
+ try {
198
+ pathAnswer = await rl.question(
199
+ `\n${BOLD}${t(lang, "prompt.projectPath")}${RESET} ${DIM}[${defaultPath}]${RESET}: `,
200
+ );
201
+ } catch {
202
+ // stdin closed — use default
203
+ }
204
+ const projectPath = pathAnswer.trim() || defaultPath;
205
+
206
+ // 9. Package manager selection
207
+ const detected = detectPm();
208
+ console.log(
209
+ `\n${BOLD}${t(lang, "prompt.pmSelect")}${RESET} ${DIM}(${t(lang, "prompt.pmDetected", { detected })})${RESET}`,
210
+ );
211
+ PM_CHOICES.forEach((pm, i) => {
212
+ const marker =
213
+ pm === detected
214
+ ? ` ${CYAN}${t(lang, "prompt.pmDetectedMarker")}${RESET}`
215
+ : "";
216
+ console.log(` ${i + 1}) ${pm}${marker}`);
217
+ });
218
+
219
+ let pmAnswer = "";
220
+ try {
221
+ pmAnswer = await rl.question(
222
+ `${t(lang, "prompt.pmChoose", { count: PM_CHOICES.length, default: detected })}: `,
223
+ );
224
+ } catch {
225
+ // stdin closed — use default
226
+ }
227
+
228
+ let pm = detected;
229
+ const pmIndex = parseInt(pmAnswer, 10);
230
+ if (pmIndex >= 1 && pmIndex <= PM_CHOICES.length) {
231
+ pm = PM_CHOICES[pmIndex - 1];
232
+ } else if (pmAnswer.trim() && PM_CHOICES.includes(pmAnswer.trim())) {
233
+ pm = pmAnswer.trim();
234
+ }
235
+
236
+ done = true;
237
+ rl.close();
238
+ return { projectName, organization, copyright, license, version, author, projectPath, pm, lang };
239
+ } catch (err) {
240
+ done = true;
241
+ rl.close();
242
+ if (err.code === "ERR_USE_AFTER_CLOSE") {
243
+ process.exit(0);
244
+ }
245
+ throw err;
246
+ }
247
+ }