@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 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
- pnpm add -g bricklayer
25
+ yarn global add @tukuyomil032/bricklayer
26
+
22
27
  # or
23
- yarn global add bricklayer
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
- // Add husky hooks entries only if requested
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());
@@ -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'
@@ -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; // ms
82
- const maxHold = 95; // hold at 95% until process completes
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
- // Advance progress a bit toward target, ensuring monotonic increase
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
- // Add a chunk that's a fraction of the remaining, clamped
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) => { stdoutChunks.push(Buffer.from(c)); onOutput(); });
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) => { stderrChunks.push(Buffer.from(c)); onOutput(); });
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) {
@@ -1,5 +1,5 @@
1
1
  const LICENSE_TEXTS = {
2
- 'MIT': `MIT License
2
+ MIT: `MIT License
3
3
 
4
4
  Copyright (c) [year], [fullname] All Rights Reserved.
5
5
 
@@ -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
  }
@@ -88,9 +88,11 @@ export async function promptProjectDetails(opts = {}) {
88
88
  ];
89
89
  const totalQuestions = questions.length;
90
90
  const answers = {};
91
- console.log(''); // Blank line before prompts
91
+ console.log('');
92
92
  const uiWith = inquirer;
93
- const bottomBar = uiWith.ui && uiWith.ui.BottomBar ? new uiWith.ui.BottomBar() : { updateBottomBar: () => { }, close: () => { } };
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
- if (confirm.confirmSel === 'reenter')
179
+ }
180
+ if (confirm.confirmSel === 'reenter') {
188
181
  continue;
189
- if (confirm.confirmSel === 'back')
190
- break; // return to navigation
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(''); // Blank line after completion
235
+ console.log('');
248
236
  return answers;
249
237
  }
@@ -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
- "# 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/_"
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
- "# 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"
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
- "# 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"
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
- "# 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
- ]
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
- "#!/bin/sh",
164
- ". \"$(dirname \"$0\")/_/husky.sh\"",
165
- "",
166
- "pnpm run lint && pnpm run format:check"
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
- const prepareScript = answers.useHusky
217
- ? `husky install && ${buildCmdForManager(mgr)}`
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 defineConfig({
366
- ignores: ['dist/'],
367
- overrides: [
368
- {
369
- files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
370
- plugins: { js },
371
- extends: ['js/recommended'],
372
- languageOptions: { globals: globals.node },
359
+ export default [
360
+ {
361
+ ignores: ['dist/**'],
362
+ },
363
+
364
+ {
365
+ files: ['**/*.{js,mjs,cjs}'],
366
+ languageOptions: {
367
+ globals: globals.node,
373
368
  },
374
- Object.assign({ files: ['**/*.{ts,mts,cts}'] }, tseslint.configs.recommended),
375
- {
376
- files: ['**/*.json'],
377
- plugins: { json },
378
- language: 'json/json',
379
- extends: ['json/recommended'],
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
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { Command } from 'commander';
3
2
  import { createCommand } from './create/index.js';
4
3
  import { sampleCommand } from './sample.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tukuyomil032/bricklayer",
3
3
  "private": false,
4
- "version": "1.0.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 install && pnpm run build",
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
  },