scaffy-tool 0.2.0 → 1.0.1

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 (45) hide show
  1. package/README.md +21 -26
  2. package/cli.js +18 -41
  3. package/core/detector.js +5 -31
  4. package/core/executor.js +5 -27
  5. package/core/interviewer.js +15 -19
  6. package/core/plugin-validator.js +1 -1
  7. package/core/registry.js +47 -28
  8. package/core/utils.js +2 -19
  9. package/package.json +11 -11
  10. package/registry/go/gin/plugin.json +23 -0
  11. package/registry/go/gin/v1/questions.js +19 -0
  12. package/registry/go/gin/v1/scaffold.js +196 -0
  13. package/registry/index.json +4 -4
  14. package/registry/javascript/expressjs/plugin.json +44 -0
  15. package/registry/javascript/expressjs/v4/questions.js +43 -0
  16. package/registry/javascript/expressjs/v4/scaffold.js +236 -0
  17. package/registry/javascript/nestjs/plugin.json +2 -2
  18. package/registry/javascript/nestjs/v10/questions.js +6 -4
  19. package/registry/javascript/nestjs/v10/scaffold.js +1 -1
  20. package/registry/javascript/nestjs/v11/questions.js +61 -0
  21. package/registry/javascript/nestjs/v11/scaffold.js +94 -0
  22. package/registry/javascript/nextjs/plugin.json +44 -0
  23. package/registry/javascript/nextjs/v14/questions.js +44 -0
  24. package/registry/javascript/nextjs/v14/scaffold.js +57 -0
  25. package/registry/javascript/vuejs/v3/questions.js +5 -3
  26. package/registry/javascript/vuejs/v3/scaffold.js +3 -10
  27. package/registry/php/laravel/plugin.json +2 -2
  28. package/registry/php/laravel/v11/questions.js +3 -3
  29. package/registry/php/laravel/v11/scaffold.js +1 -1
  30. package/registry/php/laravel/v12/questions.js +45 -0
  31. package/registry/php/laravel/v12/scaffold.js +46 -0
  32. package/registry/php/laravel/v13/questions.js +28 -0
  33. package/registry/php/laravel/v13/scaffold.js +46 -0
  34. package/registry/php/laravel/v13/test/.gitkeep +0 -0
  35. package/registry/php/laravel/v13/test/questions.test.js +48 -0
  36. package/registry/php/laravel/v13/test/scaffold.test.js +105 -0
  37. package/registry/php/symfony/plugin.json +35 -0
  38. package/registry/php/symfony/v7/questions.js +19 -0
  39. package/registry/php/symfony/v7/scaffold.js +74 -0
  40. package/registry/python/django/plugin.json +35 -0
  41. package/registry/python/django/v5/questions.js +24 -0
  42. package/registry/python/django/v5/scaffold.js +107 -0
  43. package/registry/python/fastapi/plugin.json +35 -0
  44. package/registry/python/fastapi/v1/questions.js +25 -0
  45. package/registry/python/fastapi/v1/scaffold.js +180 -0
package/README.md CHANGED
@@ -88,13 +88,13 @@ One command. A few questions. Ready to code.
88
88
 
89
89
  Existing scaffolding tools are framework-specific, opinionated, and go stale fast. Scaffy takes a fundamentally different approach.
90
90
 
91
- | | Traditional Scaffolding | Scaffy |
92
- |-----------------------------|-------------------------------|-------------------------------------|
93
- | **Multi-framework support** | ❌ One tool per framework | ✅ Every framework, one tool |
94
- | **Always up to date** | ❌ Stores stale templates | ✅ Uses official CLI commands |
95
- | **Requirement checking** | ❌ Breaks halfway through | ✅ Validates before starting |
96
- | **Community extensible** | ❌ Closed, maintainer-only | ✅ Add any framework in 3 files |
97
- | **Version management** | ❌ Usually locked to one | ✅ Multiple versions per framework |
91
+ | | Traditional Scaffolding | Scaffy |
92
+ | --------------------------- | -------------------------- | ---------------------------------- |
93
+ | **Multi-framework support** | ❌ One tool per framework | ✅ Every framework, one tool |
94
+ | **Always up to date** | ❌ Stores stale templates | ✅ Uses official CLI commands |
95
+ | **Requirement checking** | ❌ Breaks halfway through | ✅ Validates before starting |
96
+ | **Community extensible** | ❌ Closed, maintainer-only | ✅ Add any framework in 3 files |
97
+ | **Version management** | ❌ Usually locked to one | ✅ Multiple versions per framework |
98
98
 
99
99
  ---
100
100
 
@@ -142,7 +142,7 @@ scaffy --help # Help
142
142
  <td rowspan="2"><strong>PHP</strong></td>
143
143
  <td>Laravel</td>
144
144
  <td><code>scaffy laravel</code></td>
145
- <td>v10, v11</td>
145
+ <td>v11, v12, v13</td>
146
146
  </tr>
147
147
  <tr>
148
148
  <td>Symfony</td>
@@ -153,7 +153,7 @@ scaffy --help # Help
153
153
  <td rowspan="4"><strong>JavaScript</strong></td>
154
154
  <td>NestJS</td>
155
155
  <td><code>scaffy nestjs</code></td>
156
- <td>v10</td>
156
+ <td>v10, v11</td>
157
157
  </tr>
158
158
  <tr>
159
159
  <td>VueJS</td>
@@ -171,7 +171,7 @@ scaffy --help # Help
171
171
  <td>v4</td>
172
172
  </tr>
173
173
  <tr>
174
- <td rowspan="3"><strong>Python</strong></td>
174
+ <td rowspan="2"><strong>Python</strong></td>
175
175
  <td>Django</td>
176
176
  <td><code>scaffy django</code></td>
177
177
  <td>v5</td>
@@ -179,12 +179,7 @@ scaffy --help # Help
179
179
  <tr>
180
180
  <td>FastAPI</td>
181
181
  <td><code>scaffy fastapi</code></td>
182
- <td>v0.100+</td>
183
- </tr>
184
- <tr>
185
- <td>Flask</td>
186
- <td><code>scaffy flask</code></td>
187
- <td>v3</td>
182
+ <td>v1</td>
188
183
  </tr>
189
184
  <tr>
190
185
  <td><strong>Go</strong></td>
@@ -219,20 +214,16 @@ Scaffy does **not** store template files. Instead, `scaffold.js` calls the frame
219
214
  ```javascript
220
215
  // registry/php/laravel/v11/scaffold.js
221
216
  module.exports = async (answers, utils) => {
222
- const { projectName, starterKit, database } = answers
217
+ const { projectName, starterKit, database } = answers;
223
218
 
224
219
  // Step 1 — Official Laravel installer
225
- await utils.run(
226
- `composer create-project laravel/laravel ${projectName}`
227
- )
220
+ await utils.run(`composer create-project laravel/laravel ${projectName}`);
228
221
 
229
222
  // Step 2 — Layered on top with your choices
230
223
  if (starterKit === 'Breeze') {
231
- await utils.runInProject(projectName,
232
- `php artisan breeze:install`
233
- )
224
+ await utils.runInProject(projectName, `php artisan breeze:install`);
234
225
  }
235
- }
226
+ };
236
227
  ```
237
228
 
238
229
  When a new framework version releases tomorrow, a community member can add support in minutes — with zero changes to Scaffy's core.
@@ -306,7 +297,8 @@ Scaffy maintains a minimum **80% test coverage** across all core modules. Every
306
297
 
307
298
  ## 🛣️ Roadmap
308
299
 
309
- **v0.1.0 — Hello World** *(current)*
300
+ **v0.1.0 — Hello World** _(current)_
301
+
310
302
  - [x] Core engine — detector, registry, interviewer, executor
311
303
  - [x] Functional programming architecture
312
304
  - [x] Requirement checking with OS-specific install guides
@@ -315,17 +307,20 @@ Scaffy maintains a minimum **80% test coverage** across all core modules. Every
315
307
  - [x] Published on npm
316
308
 
317
309
  **v0.2.0 — Community Ready**
310
+
318
311
  - [ ] ESM migration — modern JavaScript standard
319
312
  - [ ] Plugin contribution system with automated CI validation
320
313
  - [ ] Full documentation site
321
314
  - [ ] 15+ framework plugins
322
315
 
323
316
  **v0.3.0 — Growth**
317
+
324
318
  - [ ] AI mode — describe your project in plain English
325
319
  - [ ] `scaffy add github:user/plugin` — install external plugins
326
320
  - [ ] Discord community
327
321
 
328
322
  **v1.0.0 — Ecosystem**
323
+
329
324
  - [ ] VS Code extension
330
325
  - [ ] Plugin marketplace website
331
326
  - [ ] Enterprise adoption
@@ -389,4 +384,4 @@ Built with dedication by the Scaffy community
389
384
  &nbsp;·&nbsp;
390
385
  <a href="https://github.com/TanvirHossen112/scaffy/discussions">💬 Join the discussion</a>
391
386
 
392
- </div>
387
+ </div>
package/cli.js CHANGED
@@ -1,17 +1,18 @@
1
1
  #!/usr/bin/env node
2
-
3
- const { Command } = require('commander');
4
- const chalk = require('chalk');
5
- const figlet = require('figlet');
6
- const ora = require('ora');
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import figlet from 'figlet';
5
+ import ora from 'ora';
6
+ import { createRequire } from 'module';
7
+ import * as detector from './core/detector.js';
8
+ import * as registry from './core/registry.js';
9
+ import * as interviewer from './core/interviewer.js';
10
+ import * as executor from './core/executor.js';
11
+ import { buildPluginUtils, title, error, divider } from './core/utils.js';
12
+
13
+ const require = createRequire(import.meta.url);
7
14
  const { version } = require('./package.json');
8
- const detector = require('./core/detector');
9
- const registry = require('./core/registry');
10
- const interviewer = require('./core/interviewer');
11
- const executor = require('./core/executor');
12
- const { buildPluginUtils, title, error, divider } = require('./core/utils');
13
15
 
14
- // ─── Banner ───────────────────────────────────────────
15
16
  const showBanner = () => {
16
17
  console.log(
17
18
  chalk.cyan(
@@ -24,39 +25,29 @@ const showBanner = () => {
24
25
  console.log(chalk.gray(' One command. Any framework. Ready to code.\n'));
25
26
  };
26
27
 
27
- // ─── Run Full Scaffold Flow ────────────────────────────
28
28
  const runScaffold = async framework => {
29
29
  try {
30
- // Step 1 Load plugin
31
- const version = await interviewer.askVersion(framework);
32
- const plugin = registry.loadPlugin(framework, version);
30
+ const selectedVersion = await interviewer.askVersion(framework);
31
+ const plugin = await registry.loadPlugin(framework, selectedVersion);
33
32
 
34
- // Step 2 — Check requirements
35
33
  const requirementsMet = await detector.report(plugin.meta.requires);
36
-
37
34
  if (!requirementsMet) {
38
35
  process.exit(1);
39
36
  }
40
37
 
41
- // Step 3 — Ask questions
42
38
  const answers = await interviewer.askFrameworkQuestions(framework, plugin);
43
39
 
44
- // Step 4 — Build utils for plugin
45
40
  const utils = buildPluginUtils(executor);
46
41
 
47
- // Step 5 — Run scaffold
48
42
  divider();
49
- const spinner = ora(
50
- chalk.cyan(`Scaffolding ${framework.name} project...`)
51
- ).start();
52
-
43
+ console.log(chalk.cyan(`\n🚀 Scaffolding ${framework.name} project...\n`));
53
44
  try {
54
45
  await plugin.scaffold(answers, utils);
55
- spinner.succeed(
56
- chalk.green(`${framework.name} project created successfully!`)
46
+ console.log(
47
+ chalk.green(`\n✅ ${framework.name} project created successfully!\n`)
57
48
  );
58
49
  } catch (err) {
59
- spinner.fail(chalk.red('Scaffolding failed'));
50
+ console.log(chalk.red('\n❌ Scaffolding failed\n'));
60
51
  error(err.message);
61
52
  process.exit(1);
62
53
  }
@@ -68,7 +59,6 @@ const runScaffold = async framework => {
68
59
  }
69
60
  };
70
61
 
71
- // ─── Program Setup ────────────────────────────────────
72
62
  const program = new Command();
73
63
 
74
64
  program
@@ -76,21 +66,16 @@ program
76
66
  .description('One command. Any framework. Ready to code.')
77
67
  .version(version, '-v, --version', 'Show current version');
78
68
 
79
- // ─── Default Command ───────────────────────────────────
80
69
  program
81
70
  .argument('[framework]', 'Framework name to scaffold directly')
82
71
  .action(async frameworkQuery => {
83
72
  showBanner();
84
-
85
- // Direct framework mode
86
73
  if (frameworkQuery) {
87
74
  const framework = await interviewer.askDirectFramework(frameworkQuery);
88
75
  if (!framework) process.exit(1);
89
76
  await runScaffold(framework);
90
77
  return;
91
78
  }
92
-
93
- // Interactive mode
94
79
  try {
95
80
  title('Select A Framework');
96
81
  const framework = await interviewer.askFramework();
@@ -101,7 +86,6 @@ program
101
86
  }
102
87
  });
103
88
 
104
- // ─── List Command ──────────────────────────────────────
105
89
  program
106
90
  .command('list')
107
91
  .description('List all available frameworks')
@@ -110,15 +94,12 @@ program
110
94
  registry.displayFrameworks();
111
95
  });
112
96
 
113
- // ─── Search Command ────────────────────────────────────
114
97
  program
115
98
  .command('search <query>')
116
99
  .description('Search for a framework by name or language')
117
100
  .action(query => {
118
101
  showBanner();
119
-
120
102
  const results = registry.searchFrameworks(query);
121
-
122
103
  if (results.length === 0) {
123
104
  console.log(chalk.red(`\n No frameworks found for "${query}"\n`));
124
105
  console.log(
@@ -126,18 +107,14 @@ program
126
107
  );
127
108
  return;
128
109
  }
129
-
130
110
  console.log(chalk.bold(`\n 🔍 Results for "${query}":\n`));
131
-
132
111
  results.forEach(f => {
133
112
  console.log(
134
113
  chalk.white(` • ${f.name}`) +
135
114
  chalk.gray(` (${f.language}) — scaffy ${f.alias[0]}`)
136
115
  );
137
116
  });
138
-
139
117
  console.log('');
140
118
  });
141
119
 
142
- // ─── Parse ────────────────────────────────────────────
143
120
  program.parse(process.argv);
package/core/detector.js CHANGED
@@ -1,7 +1,7 @@
1
- const { execSync } = require('child_process');
2
- const os = require('os');
3
- const semver = require('semver');
4
- const chalk = require('chalk');
1
+ import { execSync } from 'child_process';
2
+ import os from 'os';
3
+ import semver from 'semver';
4
+ import chalk from 'chalk';
5
5
 
6
6
  const getOS = () => {
7
7
  const platform = os.platform();
@@ -49,25 +49,18 @@ const buildToolResult = (tool, installed, versionOk, extra = {}) => ({
49
49
 
50
50
  const checkTool = requirement => {
51
51
  const { tool, checkCommand, parseVersion, minVersion } = requirement;
52
-
53
52
  const { success, output } = runCommand(checkCommand);
54
-
55
53
  if (!success) {
56
54
  return buildToolResult(tool, false, false);
57
55
  }
58
-
59
56
  if (!parseVersion || !minVersion) {
60
57
  return buildToolResult(tool, true, true);
61
58
  }
62
-
63
59
  const installedVersion = parseVersionFromOutput(output, parseVersion);
64
-
65
60
  if (!installedVersion) {
66
61
  return buildToolResult(tool, true, true);
67
62
  }
68
-
69
63
  const versionOk = satisfiesMinVersion(installedVersion, minVersion);
70
-
71
64
  return buildToolResult(tool, true, versionOk, {
72
65
  installedVersion,
73
66
  ...(!versionOk && { requiredVersion: minVersion }),
@@ -79,9 +72,7 @@ const checkAll = requirements => {
79
72
  ...checkTool(req),
80
73
  installGuide: req.installGuide,
81
74
  }));
82
-
83
75
  const allPassed = results.every(r => r.installed && r.versionOk);
84
-
85
76
  return { allPassed, results };
86
77
  };
87
78
 
@@ -89,7 +80,6 @@ const formatToolStatus = result => {
89
80
  if (result.installed && result.versionOk) {
90
81
  return chalk.green(` ✅ ${result.tool}`) + chalk.gray(' — found');
91
82
  }
92
-
93
83
  if (result.installed && !result.versionOk) {
94
84
  return (
95
85
  chalk.yellow(` ⚠️ ${result.tool}`) +
@@ -99,35 +89,27 @@ const formatToolStatus = result => {
99
89
  )
100
90
  );
101
91
  }
102
-
103
92
  return chalk.red(` ❌ ${result.tool}`) + chalk.gray(' — not found');
104
93
  };
105
94
 
106
95
  const formatInstallGuide = (guide, currentOS) => {
107
96
  if (!guide) return [];
108
-
109
97
  const lines = [];
110
-
111
98
  if (guide[currentOS]) {
112
99
  lines.push(chalk.cyan(` 💻 Install: `) + chalk.white(guide[currentOS]));
113
100
  }
114
-
115
101
  if (guide.docs) {
116
102
  lines.push(chalk.cyan(` 📖 Docs: `) + chalk.underline(guide.docs));
117
103
  }
118
-
119
104
  return lines;
120
105
  };
121
106
 
122
107
  const formatResult = currentOS => result => {
123
108
  const lines = [formatToolStatus(result)];
124
-
125
109
  const needsGuide = !result.installed || !result.versionOk;
126
-
127
110
  if (needsGuide) {
128
111
  lines.push(...formatInstallGuide(result.installGuide, currentOS));
129
112
  }
130
-
131
113
  return lines.join('\n');
132
114
  };
133
115
 
@@ -144,43 +126,35 @@ const formatSuccessMessage = () =>
144
126
 
145
127
  const report = requirements => {
146
128
  console.log(chalk.bold('\n🔍 Checking requirements...\n'));
147
-
148
129
  const { allPassed, results } = checkAll(requirements);
149
130
  const currentOS = getOS();
150
-
151
131
  results.map(formatResult(currentOS)).forEach(line => console.log(line));
152
-
153
132
  console.log(allPassed ? formatSuccessMessage() : formatFailureMessage());
154
-
155
133
  return allPassed;
156
134
  };
157
135
 
158
136
  const detectAvailableChoices = async choices => {
159
137
  if (!choices || choices.length === 0) return [];
160
-
161
138
  const results = await Promise.all(
162
139
  choices.map(async choice => {
163
140
  try {
164
141
  const { success, output } = runCommand(choice.checkCommand);
165
-
166
142
  if (!success || !output || output.toString().trim() === '') {
167
143
  return { ...choice, installed: false };
168
144
  }
169
-
170
145
  return { ...choice, installed: true };
171
146
  } catch {
172
147
  return { ...choice, installed: false };
173
148
  }
174
149
  })
175
150
  );
176
-
177
151
  const available = results.filter(c => c.installed);
178
152
  return available.length > 0
179
153
  ? available
180
154
  : [{ ...choices[0], installed: false }];
181
155
  };
182
156
 
183
- module.exports = {
157
+ export {
184
158
  getOS,
185
159
  runCommand,
186
160
  parseVersionFromOutput,
package/core/executor.js CHANGED
@@ -1,19 +1,16 @@
1
- const { spawn } = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const chalk = require('chalk');
1
+ import { spawn } from 'child_process';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import chalk from 'chalk';
5
5
 
6
6
  const run = command =>
7
7
  new Promise((resolve, reject) => {
8
8
  console.log(chalk.gray(`\n $ ${command}\n`));
9
-
10
9
  const [cmd, ...args] = command.split(' ');
11
-
12
10
  const child = spawn(cmd, args, {
13
11
  stdio: 'inherit',
14
12
  shell: true,
15
13
  });
16
-
17
14
  child.on('close', code => {
18
15
  if (code !== 0) {
19
16
  reject(new Error(`Command failed with exit code ${code}: ${command}`));
@@ -21,7 +18,6 @@ const run = command =>
21
18
  resolve();
22
19
  }
23
20
  });
24
-
25
21
  child.on('error', err => {
26
22
  reject(new Error(`Failed to run command: ${command}\n${err.message}`));
27
23
  });
@@ -29,16 +25,13 @@ const run = command =>
29
25
 
30
26
  const runInProject = (projectName, command) => {
31
27
  console.log(chalk.gray(`\n $ cd ${projectName} && ${command}\n`));
32
-
33
28
  return new Promise((resolve, reject) => {
34
29
  const [cmd, ...args] = command.split(' ');
35
-
36
30
  const child = spawn(cmd, args, {
37
31
  stdio: 'inherit',
38
32
  shell: true,
39
33
  cwd: path.join(process.cwd(), projectName),
40
34
  });
41
-
42
35
  child.on('close', code => {
43
36
  if (code !== 0) {
44
37
  reject(new Error(`Command failed with exit code ${code}: ${command}`));
@@ -46,7 +39,6 @@ const runInProject = (projectName, command) => {
46
39
  resolve();
47
40
  }
48
41
  });
49
-
50
42
  child.on('error', err => {
51
43
  reject(new Error(`Failed to run command: ${command}\n${err.message}`));
52
44
  });
@@ -55,13 +47,10 @@ const runInProject = (projectName, command) => {
55
47
 
56
48
  const setEnv = (projectName, vars) => {
57
49
  const envPath = path.join(process.cwd(), projectName, '.env');
58
-
59
50
  if (!fs.existsSync(envPath)) {
60
51
  throw new Error(`.env file not found in ${projectName}`);
61
52
  }
62
-
63
53
  let content = fs.readFileSync(envPath, 'utf8');
64
-
65
54
  Object.entries(vars).forEach(([key, value]) => {
66
55
  const regex = new RegExp(`^${key}=.*`, 'm');
67
56
  if (regex.test(content)) {
@@ -70,36 +59,25 @@ const setEnv = (projectName, vars) => {
70
59
  content += `\n${key}=${value}`;
71
60
  }
72
61
  });
73
-
74
62
  fs.writeFileSync(envPath, content);
75
63
  };
76
64
 
77
65
  const appendToFile = (filePath, content) => {
78
66
  const fullPath = path.join(process.cwd(), filePath);
79
67
  const dir = path.dirname(fullPath);
80
-
81
68
  if (!fs.existsSync(dir)) {
82
69
  fs.mkdirSync(dir, { recursive: true });
83
70
  }
84
-
85
71
  fs.appendFileSync(fullPath, content);
86
72
  };
87
73
 
88
74
  const createFile = (filePath, content = '') => {
89
75
  const fullPath = path.join(process.cwd(), filePath);
90
76
  const dir = path.dirname(fullPath);
91
-
92
77
  if (!fs.existsSync(dir)) {
93
78
  fs.mkdirSync(dir, { recursive: true });
94
79
  }
95
-
96
80
  fs.writeFileSync(fullPath, content);
97
81
  };
98
82
 
99
- module.exports = {
100
- run,
101
- runInProject,
102
- setEnv,
103
- appendToFile,
104
- createFile,
105
- };
83
+ export { run, runInProject, setEnv, appendToFile, createFile };
@@ -1,6 +1,6 @@
1
- const chalk = require('chalk');
2
- const inquirer = require('inquirer');
3
- const { getFrameworks, findFramework } = require('./registry');
1
+ import chalk from 'chalk';
2
+ import inquirer from 'inquirer';
3
+ import { getAvailableFrameworks, findFramework } from './registry.js';
4
4
 
5
5
  const baseQuestions = framework => [
6
6
  {
@@ -26,12 +26,15 @@ const buildQuestions = (framework, pluginQuestions) => [
26
26
  ];
27
27
 
28
28
  const buildFrameworkChoices = () =>
29
- getFrameworks().map(f => ({
30
- name:
31
- `${f.name} ${chalk.gray(`(${f.language})`)} ` +
32
- chalk.cyan(`— latest: ${f.latest}`),
33
- value: f,
34
- }));
29
+ getAvailableFrameworks().map(f => {
30
+ const versionsStr = f.versions
31
+ .map(v => (v === f.latest ? chalk.green(`${v} (latest)`) : chalk.gray(v)))
32
+ .join(chalk.gray(', '));
33
+ return {
34
+ name: `${f.name} ${chalk.gray(`(${f.language})`)} — ` + versionsStr,
35
+ value: f,
36
+ };
37
+ });
35
38
 
36
39
  const buildVersionChoices = framework =>
37
40
  framework.versions.map(v => ({
@@ -42,7 +45,7 @@ const buildVersionChoices = framework =>
42
45
  const askFramework = async () => {
43
46
  const { framework } = await inquirer.prompt([
44
47
  {
45
- type: 'list',
48
+ type: 'select',
46
49
  name: 'framework',
47
50
  message: 'Which framework?',
48
51
  choices: buildFrameworkChoices(),
@@ -55,22 +58,19 @@ const askVersion = async framework => {
55
58
  if (framework.versions.length === 1) {
56
59
  return framework.latest;
57
60
  }
58
-
59
61
  const { version } = await inquirer.prompt([
60
62
  {
61
- type: 'list',
63
+ type: 'select',
62
64
  name: 'version',
63
65
  message: 'Which version?',
64
66
  choices: buildVersionChoices(framework),
65
67
  },
66
68
  ]);
67
-
68
69
  return version;
69
70
  };
70
71
 
71
72
  const askDirectFramework = async query => {
72
73
  const framework = findFramework(query);
73
-
74
74
  if (!framework) {
75
75
  console.log(chalk.red(`\n❌ Framework "${query}" not found.\n`));
76
76
  console.log(
@@ -78,20 +78,16 @@ const askDirectFramework = async query => {
78
78
  );
79
79
  return null;
80
80
  }
81
-
82
81
  return framework;
83
82
  };
84
83
 
85
84
  const askFrameworkQuestions = async (framework, plugin) => {
86
85
  console.log(chalk.bold(`\n🚀 Setting up ${framework.name}\n`));
87
-
88
86
  const pluginQuestions =
89
87
  typeof plugin.questions === 'function'
90
88
  ? await plugin.questions()
91
89
  : plugin.questions;
92
-
93
90
  const questions = buildQuestions(framework, pluginQuestions);
94
-
95
91
  const answers = await inquirer.prompt(questions);
96
92
  return answers;
97
93
  };
@@ -125,7 +121,7 @@ const runInteractiveMode = async () => {
125
121
  }
126
122
  };
127
123
 
128
- module.exports = {
124
+ export {
129
125
  baseQuestions,
130
126
  buildQuestions,
131
127
  buildFrameworkChoices,
@@ -1,2 +1,2 @@
1
1
  // TODO: Coming in Sprint 1
2
- module.exports = {};
2
+ export default {};