@tukuyomil032/bricklayer 1.0.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.
@@ -0,0 +1,63 @@
1
+ import https from 'https';
2
+ export async function fetchLatestVersion(packageName) {
3
+ return new Promise((resolve, reject) => {
4
+ const url = `https://registry.npmjs.org/${packageName}/latest`;
5
+ https
6
+ .get(url, (res) => {
7
+ let data = '';
8
+ res.on('data', (chunk) => (data += chunk));
9
+ res.on('end', () => {
10
+ try {
11
+ const json = JSON.parse(data);
12
+ resolve(`^${json.version}`);
13
+ }
14
+ catch (e) {
15
+ reject(e);
16
+ }
17
+ });
18
+ })
19
+ .on('error', reject);
20
+ });
21
+ }
22
+ export async function getLatestVersions() {
23
+ const packages = [
24
+ 'typescript',
25
+ 'ts-node',
26
+ 'husky',
27
+ '@types/node',
28
+ 'eslint',
29
+ 'eslint-config-prettier',
30
+ 'eslint-plugin-prettier',
31
+ '@typescript-eslint/parser',
32
+ '@typescript-eslint/eslint-plugin',
33
+ 'prettier',
34
+ 'lint-staged',
35
+ 'commander',
36
+ 'inquirer',
37
+ 'chalk',
38
+ 'ora',
39
+ 'yargs',
40
+ // include package manager packages to record their latest versions
41
+ 'pnpm',
42
+ 'npm',
43
+ 'yarn',
44
+ 'bun',
45
+ ];
46
+ const versions = {};
47
+ try {
48
+ await Promise.all(packages.map(async (pkg) => {
49
+ try {
50
+ versions[pkg] = await fetchLatestVersion(pkg);
51
+ }
52
+ catch (err) {
53
+ // Fallback to a reasonable default if fetch fails
54
+ console.warn(`Failed to fetch version for ${pkg}, using fallback:`, err);
55
+ versions[pkg] = 'latest';
56
+ }
57
+ }));
58
+ }
59
+ catch (err) {
60
+ console.warn('Failed to fetch some package versions', err);
61
+ }
62
+ return versions;
63
+ }
@@ -0,0 +1,249 @@
1
+ import inquirer from 'inquirer';
2
+ import Enquirer from 'enquirer';
3
+ const Select = Enquirer.Select || Enquirer.default?.Select;
4
+ const Input = Enquirer.Input || Enquirer.default?.Input;
5
+ import chalk from 'chalk';
6
+ import readline from 'readline';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ export async function promptProjectDetails(opts = {}) {
10
+ const questions = [
11
+ {
12
+ type: 'input',
13
+ name: 'name',
14
+ message: 'Project folder name (package name):',
15
+ default: 'my-cli',
16
+ },
17
+ {
18
+ type: 'list',
19
+ name: 'moduleType',
20
+ message: 'Module system:',
21
+ choices: ['ESM', 'CommonJS'],
22
+ default: 'ESM',
23
+ },
24
+ {
25
+ type: 'list',
26
+ name: 'packageManager',
27
+ message: 'Package manager to use:',
28
+ choices: ['npm', 'pnpm', 'yarn', 'bun'],
29
+ default: 'npm',
30
+ },
31
+ {
32
+ type: 'confirm',
33
+ name: 'autoInstall',
34
+ message: 'Automatically install dependencies after scaffold? (creates lockfile)',
35
+ default: false,
36
+ },
37
+ {
38
+ type: 'input',
39
+ name: 'gitOwner',
40
+ message: "Git repository owner's user id:",
41
+ default: 'owner',
42
+ },
43
+ {
44
+ type: 'input',
45
+ name: 'gitRepo',
46
+ message: 'Git repository name:',
47
+ default: 'my-cli',
48
+ },
49
+ {
50
+ type: 'input',
51
+ name: 'npmPackageName',
52
+ message: 'npm package name (scoped or unscoped):',
53
+ default: (answers) => answers.name || 'my-cli',
54
+ },
55
+ {
56
+ type: 'input',
57
+ name: 'description',
58
+ message: 'Project description:',
59
+ default: 'A minimal TypeScript CLI',
60
+ },
61
+ {
62
+ type: 'input',
63
+ name: 'author',
64
+ message: 'Author:',
65
+ default: 'Anonymous',
66
+ },
67
+ {
68
+ type: 'list',
69
+ name: 'license',
70
+ message: 'License:',
71
+ choices: [
72
+ 'MIT',
73
+ 'Apache-2.0',
74
+ 'GPL-3.0',
75
+ 'GPL-2.0',
76
+ 'BSD-1-Clause',
77
+ 'BSD-2-Clause',
78
+ 'BSD-3-Clause',
79
+ ],
80
+ default: 'MIT',
81
+ },
82
+ {
83
+ type: 'confirm',
84
+ name: 'useHusky',
85
+ message: 'Enable Husky git hooks (pre-commit / pre-push)?',
86
+ default: true,
87
+ },
88
+ ];
89
+ const totalQuestions = questions.length;
90
+ const answers = {};
91
+ console.log(''); // Blank line before prompts
92
+ const uiWith = inquirer;
93
+ const bottomBar = uiWith.ui && uiWith.ui.BottomBar ? new uiWith.ui.BottomBar() : { updateBottomBar: () => { }, close: () => { } };
94
+ const updateProgress = (n) => {
95
+ const progressLine = `Project Scaffolding Progress: [${n}/${totalQuestions}]`;
96
+ try {
97
+ bottomBar.updateBottomBar(progressLine);
98
+ }
99
+ catch (err) {
100
+ void err;
101
+ // fallback: write using readline
102
+ readline.clearLine(process.stdout, 0);
103
+ readline.cursorTo(process.stdout, 0);
104
+ process.stdout.write(progressLine + '\n');
105
+ }
106
+ };
107
+ // Compute effective total: subtract skipped name, add destination prompt if requested
108
+ const effectiveTotal = totalQuestions - (opts.skipName ? 1 : 0) + (opts.askDestination ? 1 : 0);
109
+ let progressCount = 0;
110
+ updateProgress(progressCount);
111
+ // If askDestination is requested, prompt for it first using an interactive tree navigator
112
+ if (opts.askDestination) {
113
+ const chooseDirectoryInteractive = async (startDir) => {
114
+ let current = startDir;
115
+ while (true) {
116
+ // list visible directories (exclude hidden)
117
+ let entries = [];
118
+ try {
119
+ entries = fs.readdirSync(current).filter((name) => {
120
+ try {
121
+ return !name.startsWith('.') && fs.lstatSync(path.join(current, name)).isDirectory();
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ });
127
+ }
128
+ catch {
129
+ entries = [];
130
+ }
131
+ const choices = [
132
+ { display: `Select this directory: ${current}`, value: '__SELECT__' },
133
+ { display: '.. (go up)', value: '__UP__' },
134
+ ...entries.map((e) => ({ display: e + path.sep, value: e })),
135
+ ];
136
+ // Add a small circle at the start of each displayed choice to indicate it's selectable
137
+ choices.forEach((c) => {
138
+ c.display = `◯ ${c.display}`;
139
+ });
140
+ // Enquirer expects choice objects with `name` (unique id) and `message` (display)
141
+ const select = new Select({
142
+ name: 'dir',
143
+ message: `destination: (navigate folders, Enter to choose)`,
144
+ // Enquirer Select returns the `name` of the chosen item.
145
+ // Use `name` as the internal id (value) and `message` for display.
146
+ choices: choices.map((c) => ({ name: c.value, message: c.display })),
147
+ pageSize: 15,
148
+ });
149
+ let val;
150
+ try {
151
+ val = await select.run();
152
+ }
153
+ catch (err) {
154
+ // Enquirer may throw when TTY not available; surface a friendly error
155
+ console.error('Selection aborted or failed:', err instanceof Error ? err.message : String(err));
156
+ throw err;
157
+ }
158
+ if (val === '__SELECT__') {
159
+ // Use Enquirer's Input so the default/current path is actual editable text
160
+ while (true) {
161
+ const input = new Input({
162
+ message: 'destination: (edit or accept)',
163
+ initial: current,
164
+ });
165
+ let proposed;
166
+ try {
167
+ proposed = (await input.run()).trim();
168
+ }
169
+ catch (err) {
170
+ console.error('Input aborted:', err instanceof Error ? err.message : String(err));
171
+ throw err;
172
+ }
173
+ const confirm = await inquirer.prompt([
174
+ {
175
+ type: 'list',
176
+ name: 'confirmSel',
177
+ message: `Create project at ${proposed}?`,
178
+ choices: [
179
+ { name: 'Confirm and create here', value: 'confirm' },
180
+ { name: 'Re-enter destination', value: 'reenter' },
181
+ { name: 'Go back to folder navigation', value: 'back' },
182
+ ],
183
+ },
184
+ ]);
185
+ if (confirm.confirmSel === 'confirm')
186
+ return proposed;
187
+ if (confirm.confirmSel === 'reenter')
188
+ continue;
189
+ if (confirm.confirmSel === 'back')
190
+ break; // return to navigation
191
+ }
192
+ continue;
193
+ }
194
+ if (val === '__UP__') {
195
+ const parent = path.dirname(current);
196
+ if (parent === current) {
197
+ // already root
198
+ continue;
199
+ }
200
+ current = parent;
201
+ continue;
202
+ }
203
+ // descend into selected subdirectory
204
+ current = path.join(current, val);
205
+ }
206
+ };
207
+ // Count the destination question once and show progress before navigation
208
+ progressCount++;
209
+ updateProgress(progressCount);
210
+ const dest = await chooseDirectoryInteractive(process.cwd());
211
+ if (dest) {
212
+ answers.destination = dest;
213
+ console.log('→ Selected: ' + chalk.green(dest));
214
+ }
215
+ }
216
+ // Build list of questions to ask (skip name if requested)
217
+ const toAsk = opts.skipName ? questions.slice(1) : questions.slice();
218
+ for (let i = 0; i < toAsk.length; i++) {
219
+ const question = toAsk[i];
220
+ // Update the single progress bottom bar just before each prompt
221
+ progressCount++;
222
+ updateProgress(progressCount);
223
+ const answer = await inquirer.prompt([question]);
224
+ Object.assign(answers, answer);
225
+ // Display selected value in color for clarity (above the bottom bar)
226
+ const key = question.name;
227
+ const val = answer[key];
228
+ if (typeof val === 'string') {
229
+ console.log('→ Selected: ' + chalk.green(val));
230
+ }
231
+ else if (Array.isArray(val)) {
232
+ console.log('→ Selected: ' + val.map((v) => chalk.green(v)).join(', '));
233
+ }
234
+ else if (typeof val === 'boolean') {
235
+ console.log('→ Selected: ' + (val ? chalk.green('yes') : chalk.yellow('no')));
236
+ }
237
+ }
238
+ // Finalize progress
239
+ updateProgress(effectiveTotal);
240
+ try {
241
+ bottomBar.updateBottomBar(`Project Scaffolding Progress: [${effectiveTotal}/${effectiveTotal}] Done.`);
242
+ bottomBar.close();
243
+ }
244
+ catch (err) {
245
+ void err;
246
+ }
247
+ console.log(''); // Blank line after completion
248
+ return answers;
249
+ }