@tukuyomil032/bricklayer 1.0.0 → 1.0.2
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/README.md +22 -4
- package/dist/create/file-writer.js +2 -17
- package/dist/create/index.js +4 -10
- package/dist/create/installer.js +15 -17
- package/dist/create/licenses.js +1 -1
- package/dist/create/package-versions.js +0 -2
- package/dist/create/prompts.js +12 -24
- package/dist/create/templates.js +184 -175
- package/dist/index.js +0 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -16,17 +16,33 @@ Quickly generate a well-structured TypeScript CLI project with best practices bu
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install -g bricklayer
|
|
19
|
+
npm install -g @tukuyomil032/bricklayer
|
|
20
|
+
|
|
21
|
+
# or
|
|
22
|
+
pnpm add -g @tukuyomil032/bricklayer
|
|
23
|
+
|
|
20
24
|
# or
|
|
21
|
-
|
|
25
|
+
yarn global add @tukuyomil032/bricklayer
|
|
26
|
+
|
|
22
27
|
# or
|
|
23
|
-
|
|
28
|
+
bun add -g @tukuyomil032/bricklayer
|
|
24
29
|
```
|
|
25
30
|
|
|
26
31
|
## Usage
|
|
27
32
|
|
|
28
33
|
```bash
|
|
34
|
+
# When treating the current directory during command execution as the project's root folder:
|
|
29
35
|
bricklayer create
|
|
36
|
+
|
|
37
|
+
# If you want to specify the project's root folder yourself (we generally recommend using this option):
|
|
38
|
+
# Use the arrow keys (up and down) and the Enter key to navigate to the project's root folder.
|
|
39
|
+
bricklayer create -d
|
|
40
|
+
|
|
41
|
+
# You can also specify a project folder directly by entering a relative path after the `-d` option.
|
|
42
|
+
bricklayer create -d ~/Documents/dev/CLI/my-test-project
|
|
43
|
+
|
|
44
|
+
# available aliases
|
|
45
|
+
bl create
|
|
30
46
|
```
|
|
31
47
|
|
|
32
48
|
Follow the interactive prompts to configure your project:
|
|
@@ -34,8 +50,10 @@ Follow the interactive prompts to configure your project:
|
|
|
34
50
|
- Project name
|
|
35
51
|
- Module system (ESM / CommonJS)
|
|
36
52
|
- Package manager
|
|
53
|
+
- Automatically install dependencies(create lockfile)
|
|
37
54
|
- Git repository details
|
|
38
55
|
- Optional tools (Prettier, ESLint)
|
|
56
|
+
- husky(pre-commit, pre-push)
|
|
39
57
|
|
|
40
58
|
## Generated Project Structure
|
|
41
59
|
|
|
@@ -45,7 +63,7 @@ your-cli/
|
|
|
45
63
|
│ ├── commands/
|
|
46
64
|
│ │ └── hello.ts
|
|
47
65
|
│ └── index.ts
|
|
48
|
-
├── .husky/
|
|
66
|
+
├── .husky/ #options
|
|
49
67
|
│ ├── pre-commit
|
|
50
68
|
│ └── pre-push
|
|
51
69
|
├── .gitignore
|
|
@@ -16,77 +16,62 @@ export async function writeProjectFiles(targetDir, answers, versions) {
|
|
|
16
16
|
'src/commands/hello.ts',
|
|
17
17
|
'README.md',
|
|
18
18
|
'.gitignore',
|
|
19
|
-
// husky hooks are optional and added conditionally below
|
|
20
19
|
'.prettierignore',
|
|
21
20
|
'.npmignore',
|
|
22
21
|
'.editorconfig',
|
|
23
22
|
'LICENSE',
|
|
24
23
|
];
|
|
25
|
-
// Always include ESLint and Prettier config files by default
|
|
26
24
|
tasks.push('.prettierrc');
|
|
27
25
|
tasks.push('eslint.config.js');
|
|
28
26
|
// Add .npmrc when using pnpm
|
|
29
27
|
const shouldAddNpmrc = answers.packageManager === 'pnpm';
|
|
30
|
-
if (shouldAddNpmrc)
|
|
28
|
+
if (shouldAddNpmrc) {
|
|
31
29
|
tasks.push('.npmrc');
|
|
32
|
-
|
|
30
|
+
}
|
|
33
31
|
if (answers.useHusky) {
|
|
34
32
|
tasks.unshift('.husky/pre-push');
|
|
35
33
|
tasks.unshift('.husky/pre-commit');
|
|
36
34
|
}
|
|
37
35
|
progressBar.start(tasks.length, 0);
|
|
38
36
|
let completed = 0;
|
|
39
|
-
// Create directory structure
|
|
40
37
|
await fs.mkdir(targetDir, { recursive: true });
|
|
41
38
|
await fs.mkdir(path.join(targetDir, 'src', 'commands'), { recursive: true });
|
|
42
39
|
if (answers.useHusky) {
|
|
43
40
|
await fs.mkdir(path.join(targetDir, '.husky'), { recursive: true });
|
|
44
41
|
}
|
|
45
|
-
// Write package.json
|
|
46
42
|
const pkg = templates.generatePackageJson(answers, versions);
|
|
47
43
|
await fs.writeFile(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
48
44
|
progressBar.update(++completed);
|
|
49
|
-
// Write tsconfig.json
|
|
50
45
|
const tsconfig = templates.generateTsConfig(answers);
|
|
51
46
|
await fs.writeFile(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
52
47
|
progressBar.update(++completed);
|
|
53
|
-
// Write source files
|
|
54
48
|
await fs.writeFile(path.join(targetDir, 'src', 'index.ts'), templates.generateIndexTs(answers));
|
|
55
49
|
progressBar.update(++completed);
|
|
56
50
|
await fs.writeFile(path.join(targetDir, 'src', 'commands', 'hello.ts'), templates.generateHelloCommandTs());
|
|
57
51
|
progressBar.update(++completed);
|
|
58
|
-
// Write README
|
|
59
52
|
await fs.writeFile(path.join(targetDir, 'README.md'), templates.generateReadme(answers));
|
|
60
53
|
progressBar.update(++completed);
|
|
61
|
-
// Write .gitignore
|
|
62
54
|
await fs.writeFile(path.join(targetDir, '.gitignore'), templates.generateGitignore());
|
|
63
55
|
progressBar.update(++completed);
|
|
64
|
-
// Write Husky hooks (only if requested)
|
|
65
56
|
if (answers.useHusky) {
|
|
66
57
|
await fs.writeFile(path.join(targetDir, '.husky', 'pre-commit'), templates.generatePreCommitHook());
|
|
67
58
|
progressBar.update(++completed);
|
|
68
59
|
await fs.writeFile(path.join(targetDir, '.husky', 'pre-push'), templates.generatePrePushHook());
|
|
69
60
|
progressBar.update(++completed);
|
|
70
61
|
}
|
|
71
|
-
// Always add .prettierignore
|
|
72
62
|
await fs.writeFile(path.join(targetDir, '.prettierignore'), templates.generatePrettierIgnore());
|
|
73
63
|
progressBar.update(++completed);
|
|
74
|
-
// Always add .npmignore
|
|
75
64
|
await fs.writeFile(path.join(targetDir, '.npmignore'), templates.generateNpmIgnore());
|
|
76
65
|
progressBar.update(++completed);
|
|
77
|
-
// Conditionally add .npmrc for pnpm
|
|
78
66
|
if (shouldAddNpmrc) {
|
|
79
67
|
await fs.writeFile(path.join(targetDir, '.npmrc'), templates.generateNpmrc());
|
|
80
68
|
progressBar.update(++completed);
|
|
81
69
|
}
|
|
82
|
-
// Always add .editorconfig
|
|
83
70
|
await fs.writeFile(path.join(targetDir, '.editorconfig'), templates.generateEditorConfig());
|
|
84
71
|
progressBar.update(++completed);
|
|
85
|
-
// Write LICENSE
|
|
86
72
|
const licenseText = await templates.generateLicenseText(answers.license, answers.author, new Date().getFullYear());
|
|
87
73
|
await fs.writeFile(path.join(targetDir, 'LICENSE'), licenseText);
|
|
88
74
|
progressBar.update(++completed);
|
|
89
|
-
// Write Prettier and ESLint configs (default included)
|
|
90
75
|
await fs.writeFile(path.join(targetDir, '.prettierrc'), templates.generatePrettierConfig());
|
|
91
76
|
progressBar.update(++completed);
|
|
92
77
|
await fs.writeFile(path.join(targetDir, 'eslint.config.js'), templates.generateEslintConfig());
|
package/dist/create/index.js
CHANGED
|
@@ -14,36 +14,32 @@ export function createCommand() {
|
|
|
14
14
|
.option('-d, --destination [path]', 'Project destination directory');
|
|
15
15
|
cmd.action(async (options) => {
|
|
16
16
|
console.log(chalk.green('Welcome to bricklayer — TypeScript CLI scaffold generator'));
|
|
17
|
-
// Show spinner
|
|
18
17
|
const initSpinner = ora('Initializing project setup...').start();
|
|
19
|
-
// Small delay for better UX
|
|
20
18
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
21
19
|
initSpinner.stop();
|
|
22
|
-
// Determine destination behavior
|
|
23
20
|
const flagProvided = Boolean(options.destination);
|
|
24
21
|
const flagHasArg = typeof options.destination === 'string';
|
|
25
22
|
const askDestination = flagProvided && !flagHasArg;
|
|
26
|
-
// Prompt once: if -d present, always skip the `name` question; if -d without arg, also ask destination interactively first
|
|
27
23
|
const answers = await promptProjectDetails({ skipName: flagProvided, askDestination });
|
|
28
|
-
// Resolve target directory
|
|
29
24
|
let target;
|
|
30
25
|
if (flagHasArg) {
|
|
31
26
|
const dest = options.destination.replace(/^~/, os.homedir());
|
|
32
27
|
target = path.resolve(dest);
|
|
33
|
-
if (!answers.name)
|
|
28
|
+
if (!answers.name) {
|
|
34
29
|
answers.name = path.basename(target);
|
|
30
|
+
}
|
|
35
31
|
}
|
|
36
32
|
else if (answers.destination) {
|
|
37
33
|
const dest = answers.destination.replace(/^~/, os.homedir());
|
|
38
34
|
target = path.resolve(dest);
|
|
39
|
-
if (!answers.name)
|
|
35
|
+
if (!answers.name) {
|
|
40
36
|
answers.name = path.basename(target);
|
|
37
|
+
}
|
|
41
38
|
}
|
|
42
39
|
else {
|
|
43
40
|
const baseDir = process.cwd();
|
|
44
41
|
target = path.resolve(baseDir, answers.name);
|
|
45
42
|
}
|
|
46
|
-
// Fetch latest package versions
|
|
47
43
|
const versionSpinner = ora('Fetching latest package versions...').start();
|
|
48
44
|
let versions;
|
|
49
45
|
try {
|
|
@@ -58,14 +54,12 @@ export function createCommand() {
|
|
|
58
54
|
try {
|
|
59
55
|
await writeProjectFiles(target, answers, versions);
|
|
60
56
|
fileSpinner.succeed('Project scaffold created at ' + target);
|
|
61
|
-
// Install dependencies if user opted in
|
|
62
57
|
if (answers.autoInstall) {
|
|
63
58
|
await installDependencies(target, answers.packageManager);
|
|
64
59
|
}
|
|
65
60
|
else {
|
|
66
61
|
console.log(chalk.yellow('Dependencies were not installed automatically.'));
|
|
67
62
|
}
|
|
68
|
-
// Show next steps
|
|
69
63
|
console.log(chalk.blue('Next steps:'));
|
|
70
64
|
console.log(` - cd ${answers.name}`);
|
|
71
65
|
const buildCmd = answers.packageManager === 'pnpm'
|
package/dist/create/installer.js
CHANGED
|
@@ -25,7 +25,6 @@ export async function installDependencies(targetDir, packageManager) {
|
|
|
25
25
|
const bin = mgr;
|
|
26
26
|
if (!(await isCommandAvailable(bin))) {
|
|
27
27
|
spinner.fail(`${bin} not found on PATH.`);
|
|
28
|
-
// Try sensible fallback order
|
|
29
28
|
const fallbacks = ['pnpm', 'npm'];
|
|
30
29
|
let usedFallback = null;
|
|
31
30
|
for (const f of fallbacks) {
|
|
@@ -72,21 +71,16 @@ function runCommandWithProgress(command, cwd) {
|
|
|
72
71
|
barsize: 40,
|
|
73
72
|
}, cliProgress.Presets.shades_classic);
|
|
74
73
|
bar.start(100, 0);
|
|
75
|
-
// Progress state
|
|
76
74
|
let progress = 0;
|
|
77
75
|
let lastRenderedFloor = 0;
|
|
78
76
|
const start = Date.now();
|
|
79
|
-
// Interval drives internal progress target; we only redraw when integer percent changes
|
|
80
77
|
let lastOutputAt = 0;
|
|
81
|
-
const tickInterval = 150;
|
|
82
|
-
const maxHold = 95;
|
|
78
|
+
const tickInterval = 150;
|
|
79
|
+
const maxHold = 95;
|
|
83
80
|
const timer = setInterval(() => {
|
|
84
81
|
const elapsed = Date.now() - start;
|
|
85
|
-
// Ease-out target that slowly approaches maxHold
|
|
86
82
|
const target = maxHold * (1 - Math.exp(-elapsed / 6000));
|
|
87
|
-
|
|
88
|
-
// If we've recently seen installer output, move faster
|
|
89
|
-
const sinceOutput = lastOutputAt ? (Date.now() - lastOutputAt) : Infinity;
|
|
83
|
+
const sinceOutput = lastOutputAt ? Date.now() - lastOutputAt : Infinity;
|
|
90
84
|
const speedMultiplier = sinceOutput < 1000 ? 2.0 : 1.0;
|
|
91
85
|
progress = Math.min(target, progress + 0.6 * speedMultiplier);
|
|
92
86
|
const floor = Math.floor(progress);
|
|
@@ -103,11 +97,10 @@ function runCommandWithProgress(command, cwd) {
|
|
|
103
97
|
const stderrChunks = [];
|
|
104
98
|
const onOutput = () => {
|
|
105
99
|
lastOutputAt = Date.now();
|
|
106
|
-
// Aggressively advance progress toward maxHold on real installer output
|
|
107
100
|
const remaining = maxHold - progress;
|
|
108
|
-
if (remaining <= 0)
|
|
101
|
+
if (remaining <= 0) {
|
|
109
102
|
return;
|
|
110
|
-
|
|
103
|
+
}
|
|
111
104
|
const advance = Math.min(remaining, Math.max(4, Math.round(remaining * 0.18)));
|
|
112
105
|
progress = progress + advance;
|
|
113
106
|
const floor = Math.floor(progress);
|
|
@@ -120,9 +113,15 @@ function runCommandWithProgress(command, cwd) {
|
|
|
120
113
|
}
|
|
121
114
|
};
|
|
122
115
|
if (child.stdout)
|
|
123
|
-
child.stdout.on('data', (c) => {
|
|
116
|
+
child.stdout.on('data', (c) => {
|
|
117
|
+
stdoutChunks.push(Buffer.from(c));
|
|
118
|
+
onOutput();
|
|
119
|
+
});
|
|
124
120
|
if (child.stderr)
|
|
125
|
-
child.stderr.on('data', (c) => {
|
|
121
|
+
child.stderr.on('data', (c) => {
|
|
122
|
+
stderrChunks.push(Buffer.from(c));
|
|
123
|
+
onOutput();
|
|
124
|
+
});
|
|
126
125
|
child.on('error', (err) => {
|
|
127
126
|
clearInterval(timer);
|
|
128
127
|
try {
|
|
@@ -133,7 +132,6 @@ function runCommandWithProgress(command, cwd) {
|
|
|
133
132
|
});
|
|
134
133
|
child.on('close', (code) => {
|
|
135
134
|
clearInterval(timer);
|
|
136
|
-
// Smoothly ramp to 100% to avoid a sudden jump
|
|
137
135
|
const finishInterval = 40; // ms
|
|
138
136
|
const finishTimer = setInterval(() => {
|
|
139
137
|
const remaining = 100 - progress;
|
|
@@ -144,14 +142,14 @@ function runCommandWithProgress(command, cwd) {
|
|
|
144
142
|
}
|
|
145
143
|
catch { }
|
|
146
144
|
clearInterval(finishTimer);
|
|
147
|
-
if (code === 0)
|
|
145
|
+
if (code === 0) {
|
|
148
146
|
return resolve();
|
|
147
|
+
}
|
|
149
148
|
const out = Buffer.concat(stdoutChunks).toString('utf8');
|
|
150
149
|
const errOut = Buffer.concat(stderrChunks).toString('utf8');
|
|
151
150
|
const e = new Error(`Command exited with code ${code}\n${errOut || out}`);
|
|
152
151
|
return reject(e);
|
|
153
152
|
}
|
|
154
|
-
// advance by a fraction of remaining to create ease-out
|
|
155
153
|
progress = progress + Math.max(1, Math.round(remaining * 0.18));
|
|
156
154
|
const floor = Math.floor(progress);
|
|
157
155
|
if (floor > lastRenderedFloor) {
|
package/dist/create/licenses.js
CHANGED
|
@@ -37,7 +37,6 @@ export async function getLatestVersions() {
|
|
|
37
37
|
'chalk',
|
|
38
38
|
'ora',
|
|
39
39
|
'yargs',
|
|
40
|
-
// include package manager packages to record their latest versions
|
|
41
40
|
'pnpm',
|
|
42
41
|
'npm',
|
|
43
42
|
'yarn',
|
|
@@ -50,7 +49,6 @@ export async function getLatestVersions() {
|
|
|
50
49
|
versions[pkg] = await fetchLatestVersion(pkg);
|
|
51
50
|
}
|
|
52
51
|
catch (err) {
|
|
53
|
-
// Fallback to a reasonable default if fetch fails
|
|
54
52
|
console.warn(`Failed to fetch version for ${pkg}, using fallback:`, err);
|
|
55
53
|
versions[pkg] = 'latest';
|
|
56
54
|
}
|
package/dist/create/prompts.js
CHANGED
|
@@ -88,9 +88,11 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
88
88
|
];
|
|
89
89
|
const totalQuestions = questions.length;
|
|
90
90
|
const answers = {};
|
|
91
|
-
console.log('');
|
|
91
|
+
console.log('');
|
|
92
92
|
const uiWith = inquirer;
|
|
93
|
-
const bottomBar = uiWith.ui && uiWith.ui.BottomBar
|
|
93
|
+
const bottomBar = uiWith.ui && uiWith.ui.BottomBar
|
|
94
|
+
? new uiWith.ui.BottomBar()
|
|
95
|
+
: { updateBottomBar: () => { }, close: () => { } };
|
|
94
96
|
const updateProgress = (n) => {
|
|
95
97
|
const progressLine = `Project Scaffolding Progress: [${n}/${totalQuestions}]`;
|
|
96
98
|
try {
|
|
@@ -98,22 +100,18 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
98
100
|
}
|
|
99
101
|
catch (err) {
|
|
100
102
|
void err;
|
|
101
|
-
// fallback: write using readline
|
|
102
103
|
readline.clearLine(process.stdout, 0);
|
|
103
104
|
readline.cursorTo(process.stdout, 0);
|
|
104
105
|
process.stdout.write(progressLine + '\n');
|
|
105
106
|
}
|
|
106
107
|
};
|
|
107
|
-
// Compute effective total: subtract skipped name, add destination prompt if requested
|
|
108
108
|
const effectiveTotal = totalQuestions - (opts.skipName ? 1 : 0) + (opts.askDestination ? 1 : 0);
|
|
109
109
|
let progressCount = 0;
|
|
110
110
|
updateProgress(progressCount);
|
|
111
|
-
// If askDestination is requested, prompt for it first using an interactive tree navigator
|
|
112
111
|
if (opts.askDestination) {
|
|
113
112
|
const chooseDirectoryInteractive = async (startDir) => {
|
|
114
113
|
let current = startDir;
|
|
115
114
|
while (true) {
|
|
116
|
-
// list visible directories (exclude hidden)
|
|
117
115
|
let entries = [];
|
|
118
116
|
try {
|
|
119
117
|
entries = fs.readdirSync(current).filter((name) => {
|
|
@@ -133,16 +131,12 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
133
131
|
{ display: '.. (go up)', value: '__UP__' },
|
|
134
132
|
...entries.map((e) => ({ display: e + path.sep, value: e })),
|
|
135
133
|
];
|
|
136
|
-
// Add a small circle at the start of each displayed choice to indicate it's selectable
|
|
137
134
|
choices.forEach((c) => {
|
|
138
135
|
c.display = `◯ ${c.display}`;
|
|
139
136
|
});
|
|
140
|
-
// Enquirer expects choice objects with `name` (unique id) and `message` (display)
|
|
141
137
|
const select = new Select({
|
|
142
138
|
name: 'dir',
|
|
143
139
|
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
140
|
choices: choices.map((c) => ({ name: c.value, message: c.display })),
|
|
147
141
|
pageSize: 15,
|
|
148
142
|
});
|
|
@@ -151,12 +145,10 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
151
145
|
val = await select.run();
|
|
152
146
|
}
|
|
153
147
|
catch (err) {
|
|
154
|
-
// Enquirer may throw when TTY not available; surface a friendly error
|
|
155
148
|
console.error('Selection aborted or failed:', err instanceof Error ? err.message : String(err));
|
|
156
149
|
throw err;
|
|
157
150
|
}
|
|
158
151
|
if (val === '__SELECT__') {
|
|
159
|
-
// Use Enquirer's Input so the default/current path is actual editable text
|
|
160
152
|
while (true) {
|
|
161
153
|
const input = new Input({
|
|
162
154
|
message: 'destination: (edit or accept)',
|
|
@@ -182,29 +174,29 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
182
174
|
],
|
|
183
175
|
},
|
|
184
176
|
]);
|
|
185
|
-
if (confirm.confirmSel === 'confirm')
|
|
177
|
+
if (confirm.confirmSel === 'confirm') {
|
|
186
178
|
return proposed;
|
|
187
|
-
|
|
179
|
+
}
|
|
180
|
+
if (confirm.confirmSel === 'reenter') {
|
|
188
181
|
continue;
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
}
|
|
183
|
+
if (confirm.confirmSel === 'back') {
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
191
186
|
}
|
|
192
187
|
continue;
|
|
193
188
|
}
|
|
194
189
|
if (val === '__UP__') {
|
|
195
190
|
const parent = path.dirname(current);
|
|
196
191
|
if (parent === current) {
|
|
197
|
-
// already root
|
|
198
192
|
continue;
|
|
199
193
|
}
|
|
200
194
|
current = parent;
|
|
201
195
|
continue;
|
|
202
196
|
}
|
|
203
|
-
// descend into selected subdirectory
|
|
204
197
|
current = path.join(current, val);
|
|
205
198
|
}
|
|
206
199
|
};
|
|
207
|
-
// Count the destination question once and show progress before navigation
|
|
208
200
|
progressCount++;
|
|
209
201
|
updateProgress(progressCount);
|
|
210
202
|
const dest = await chooseDirectoryInteractive(process.cwd());
|
|
@@ -213,16 +205,13 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
213
205
|
console.log('→ Selected: ' + chalk.green(dest));
|
|
214
206
|
}
|
|
215
207
|
}
|
|
216
|
-
// Build list of questions to ask (skip name if requested)
|
|
217
208
|
const toAsk = opts.skipName ? questions.slice(1) : questions.slice();
|
|
218
209
|
for (let i = 0; i < toAsk.length; i++) {
|
|
219
210
|
const question = toAsk[i];
|
|
220
|
-
// Update the single progress bottom bar just before each prompt
|
|
221
211
|
progressCount++;
|
|
222
212
|
updateProgress(progressCount);
|
|
223
213
|
const answer = await inquirer.prompt([question]);
|
|
224
214
|
Object.assign(answers, answer);
|
|
225
|
-
// Display selected value in color for clarity (above the bottom bar)
|
|
226
215
|
const key = question.name;
|
|
227
216
|
const val = answer[key];
|
|
228
217
|
if (typeof val === 'string') {
|
|
@@ -235,7 +224,6 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
235
224
|
console.log('→ Selected: ' + (val ? chalk.green('yes') : chalk.yellow('no')));
|
|
236
225
|
}
|
|
237
226
|
}
|
|
238
|
-
// Finalize progress
|
|
239
227
|
updateProgress(effectiveTotal);
|
|
240
228
|
try {
|
|
241
229
|
bottomBar.updateBottomBar(`Project Scaffolding Progress: [${effectiveTotal}/${effectiveTotal}] Done.`);
|
|
@@ -244,6 +232,6 @@ export async function promptProjectDetails(opts = {}) {
|
|
|
244
232
|
catch (err) {
|
|
245
233
|
void err;
|
|
246
234
|
}
|
|
247
|
-
console.log('');
|
|
235
|
+
console.log('');
|
|
248
236
|
return answers;
|
|
249
237
|
}
|
package/dist/create/templates.js
CHANGED
|
@@ -2,169 +2,164 @@ import LICENSE_TEXTS from './licenses.js';
|
|
|
2
2
|
// Embedded templates (inlined so builds include templates without copying files)
|
|
3
3
|
const staticTemplates = {
|
|
4
4
|
gitignore: [
|
|
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
|
-
|
|
5
|
+
'# Dependencies',
|
|
6
|
+
'node_modules/',
|
|
7
|
+
'bun.lockb',
|
|
8
|
+
'',
|
|
9
|
+
'# Build output',
|
|
10
|
+
'dist/',
|
|
11
|
+
'',
|
|
12
|
+
'# Environment files',
|
|
13
|
+
'.env',
|
|
14
|
+
'.env.local',
|
|
15
|
+
'.env.*.local',
|
|
16
|
+
'',
|
|
17
|
+
'# macOS',
|
|
18
|
+
'.DS_Store',
|
|
19
|
+
'.AppleDouble',
|
|
20
|
+
'.LSOverride',
|
|
21
|
+
'',
|
|
22
|
+
'# IDE',
|
|
23
|
+
'.vscode/',
|
|
24
|
+
'.idea/',
|
|
25
|
+
'*.swp',
|
|
26
|
+
'*.swo',
|
|
27
|
+
'*~',
|
|
28
|
+
'',
|
|
29
|
+
'# Logs',
|
|
30
|
+
'logs/',
|
|
31
|
+
'*.log',
|
|
32
|
+
'npm-debug.log*',
|
|
33
|
+
'yarn-debug.log*',
|
|
34
|
+
'yarn-error.log*',
|
|
35
|
+
'',
|
|
36
|
+
'# Temporary files',
|
|
37
|
+
'*.tmp',
|
|
38
|
+
'.temp/',
|
|
39
|
+
'temp/',
|
|
40
|
+
'',
|
|
41
|
+
'# Linter and formatter cache',
|
|
42
|
+
'.eslintcache',
|
|
43
|
+
'.prettier-cache',
|
|
44
|
+
'',
|
|
45
|
+
'# Husky',
|
|
46
|
+
'.husky/_',
|
|
47
47
|
],
|
|
48
48
|
prettierignore: [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
'# Dependencies',
|
|
50
|
+
'node_modules',
|
|
51
|
+
'',
|
|
52
|
+
'# Build output',
|
|
53
|
+
'dist',
|
|
54
|
+
'',
|
|
55
|
+
'# Lock files',
|
|
56
|
+
'bun.lockb',
|
|
57
|
+
'package-lock.json',
|
|
58
|
+
'yarn.lock',
|
|
59
|
+
'pnpm-lock.yaml',
|
|
60
|
+
'',
|
|
61
|
+
'# Logs',
|
|
62
|
+
'*.log',
|
|
63
|
+
'',
|
|
64
|
+
'# Coverage',
|
|
65
|
+
'coverage',
|
|
66
66
|
],
|
|
67
67
|
npmignore: [
|
|
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
|
-
|
|
68
|
+
'# Source files',
|
|
69
|
+
'src/',
|
|
70
|
+
'tsconfig.json',
|
|
71
|
+
'eslint.config.js',
|
|
72
|
+
'.prettierrc',
|
|
73
|
+
'.prettierignore',
|
|
74
|
+
'.editorconfig',
|
|
75
|
+
'',
|
|
76
|
+
'# Tests and development',
|
|
77
|
+
'*.test.ts',
|
|
78
|
+
'*.spec.ts',
|
|
79
|
+
'coverage/',
|
|
80
|
+
'.nyc_output/',
|
|
81
|
+
'',
|
|
82
|
+
'# Git and CI',
|
|
83
|
+
'.git/',
|
|
84
|
+
'.github/',
|
|
85
|
+
'.gitignore',
|
|
86
|
+
'.husky/',
|
|
87
|
+
'',
|
|
88
|
+
'# IDE',
|
|
89
|
+
'.vscode/',
|
|
90
|
+
'.idea/',
|
|
91
|
+
'*.swp',
|
|
92
|
+
'*.swo',
|
|
93
|
+
'*~',
|
|
94
|
+
'',
|
|
95
|
+
'# Logs',
|
|
96
|
+
'logs/',
|
|
97
|
+
'*.log',
|
|
98
|
+
'npm-debug.log*',
|
|
99
|
+
'yarn-debug.log*',
|
|
100
|
+
'yarn-error.log*',
|
|
101
|
+
'',
|
|
102
|
+
'# Lock files',
|
|
103
|
+
'bun.lock',
|
|
104
|
+
'package-lock.json',
|
|
105
|
+
'yarn.lock',
|
|
106
|
+
'pnpm-lock.yaml',
|
|
107
|
+
'',
|
|
108
|
+
'# macOS',
|
|
109
|
+
'.DS_Store',
|
|
110
|
+
'.AppleDouble',
|
|
111
|
+
'.LSOverride',
|
|
112
|
+
'',
|
|
113
|
+
'# Temporary files',
|
|
114
|
+
'*.tmp',
|
|
115
|
+
'.temp/',
|
|
116
|
+
'temp/',
|
|
117
|
+
'',
|
|
118
|
+
'# Development',
|
|
119
|
+
'README.dev.md',
|
|
120
|
+
'CONTRIBUTING.md',
|
|
121
|
+
'',
|
|
122
|
+
'# Documentation',
|
|
123
|
+
'docs/',
|
|
124
|
+
'',
|
|
125
|
+
'# Zip files',
|
|
126
|
+
'*.zip',
|
|
127
127
|
],
|
|
128
128
|
editorconfig: [
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
]
|
|
129
|
+
'# EditorConfig is awesome: https://EditorConfig.org',
|
|
130
|
+
'',
|
|
131
|
+
'root = true',
|
|
132
|
+
'',
|
|
133
|
+
'[*]',
|
|
134
|
+
'charset = utf-8',
|
|
135
|
+
'end_of_line = lf',
|
|
136
|
+
'insert_final_newline = true',
|
|
137
|
+
'trim_trailing_whitespace = true',
|
|
138
|
+
'indent_style = space',
|
|
139
|
+
'indent_size = 2',
|
|
140
|
+
'',
|
|
141
|
+
'[*.{js,ts}]',
|
|
142
|
+
'indent_style = space',
|
|
143
|
+
'indent_size = 2',
|
|
144
|
+
'',
|
|
145
|
+
'[*.{json,yml,yaml}]',
|
|
146
|
+
'indent_style = space',
|
|
147
|
+
'indent_size = 2',
|
|
148
|
+
'',
|
|
149
|
+
'[*.md]',
|
|
150
|
+
'trim_trailing_whitespace = false',
|
|
151
|
+
'max_line_length = off',
|
|
152
|
+
'',
|
|
153
|
+
],
|
|
154
154
|
};
|
|
155
155
|
const hooksTemplates = {
|
|
156
|
-
'pre-commit': [
|
|
157
|
-
"#!/bin/sh",
|
|
158
|
-
". \"$(dirname \"$0\")/_/husky.sh\"",
|
|
159
|
-
"",
|
|
160
|
-
"pnpm run lint-staged"
|
|
161
|
-
],
|
|
156
|
+
'pre-commit': ['#!/bin/sh', '. "$(dirname "$0")/_/husky.sh"', '', 'pnpm run lint-staged'],
|
|
162
157
|
'pre-push': [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
]
|
|
158
|
+
'#!/bin/sh',
|
|
159
|
+
'. "$(dirname "$0")/_/husky.sh"',
|
|
160
|
+
'',
|
|
161
|
+
'pnpm run lint && pnpm run format:check',
|
|
162
|
+
],
|
|
168
163
|
};
|
|
169
164
|
// license texts are provided from src/create/licenses.ts
|
|
170
165
|
export function generatePackageJson(answers, versions) {
|
|
@@ -213,9 +208,8 @@ export function generatePackageJson(answers, versions) {
|
|
|
213
208
|
return 'bun run build';
|
|
214
209
|
return `${m} run build`;
|
|
215
210
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
: buildCmdForManager(mgr);
|
|
211
|
+
// When Husky is enabled, keep prepare as just 'husky' (user requested).
|
|
212
|
+
const prepareScript = answers.useHusky ? 'husky' : buildCmdForManager(mgr);
|
|
219
213
|
return {
|
|
220
214
|
name: answers.npmPackageName || answers.name,
|
|
221
215
|
private: false,
|
|
@@ -237,6 +231,7 @@ export function generatePackageJson(answers, versions) {
|
|
|
237
231
|
typecheck: 'tsc --noEmit',
|
|
238
232
|
lint: 'eslint "src/**/*.ts"',
|
|
239
233
|
'lint:fix': 'eslint "src/**/*.ts" --fix',
|
|
234
|
+
'lint-staged': 'lint-staged',
|
|
240
235
|
format: 'prettier --write "src/**/*.ts"',
|
|
241
236
|
'format:check': 'prettier --check "src/**/*.ts"',
|
|
242
237
|
}, answers.useHusky ? {} : {}),
|
|
@@ -360,26 +355,41 @@ export function generateEslintConfig() {
|
|
|
360
355
|
import globals from 'globals';
|
|
361
356
|
import tseslint from 'typescript-eslint';
|
|
362
357
|
import json from '@eslint/json';
|
|
363
|
-
import { defineConfig } from 'eslint/config';
|
|
364
358
|
|
|
365
|
-
export default
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
359
|
+
export default [
|
|
360
|
+
{
|
|
361
|
+
ignores: ['dist/**'],
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
{
|
|
365
|
+
files: ['**/*.{js,mjs,cjs}'],
|
|
366
|
+
languageOptions: {
|
|
367
|
+
globals: globals.node,
|
|
373
368
|
},
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
369
|
+
...js.configs.recommended,
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
{
|
|
373
|
+
files: ['**/*.{ts,mts,cts}'],
|
|
374
|
+
languageOptions: {
|
|
375
|
+
globals: globals.node,
|
|
376
|
+
parser: tseslint.parser,
|
|
377
|
+
},
|
|
378
|
+
plugins: {
|
|
379
|
+
'@typescript-eslint': tseslint.plugin,
|
|
380
380
|
},
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
rules: {
|
|
382
|
+
...tseslint.configs.recommended.rules,
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
{
|
|
387
|
+
files: ['**/*.json'],
|
|
388
|
+
language: 'json/json',
|
|
389
|
+
plugins: { json },
|
|
390
|
+
...json.configs.recommended,
|
|
391
|
+
},
|
|
392
|
+
];
|
|
383
393
|
`;
|
|
384
394
|
}
|
|
385
395
|
export async function generateLicenseText(license, author, year) {
|
|
@@ -407,4 +417,3 @@ export async function generateLicenseText(license, author, year) {
|
|
|
407
417
|
.replace(fullnameRegex, author)
|
|
408
418
|
.replace(nameRegex, author);
|
|
409
419
|
}
|
|
410
|
-
// Note: .eslintignore generation removed in favor of eslint.config.js ignores
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tukuyomil032/bricklayer",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "Interactive TypeScript CLI project scaffolder",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -15,13 +15,14 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
18
|
-
"prepare": "husky
|
|
18
|
+
"prepare": "husky",
|
|
19
19
|
"prepublishOnly": "pnpm run build",
|
|
20
20
|
"dev": "ts-node --esm src/index.ts",
|
|
21
21
|
"start": "node dist/index.js",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
23
|
"lint": "eslint \"src/**/*.ts\"",
|
|
24
24
|
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
25
|
+
"lint-staged": "lint-staged",
|
|
25
26
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
26
27
|
"format:check": "prettier --check \"src/**/*.ts\""
|
|
27
28
|
},
|