spyne-cli 0.5.5 → 0.6.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.
package/index.js CHANGED
@@ -1,30 +1,31 @@
1
1
  #!/usr/bin/env node
2
- import {SpyneCliUI} from './src/ui.js';
2
+
3
+ import { SpyneCliUI } from './src/ui.js';
3
4
  import clear from 'clear';
4
- import SpyneAppCreator from './src/spyne-app-creator.js';
5
- import SpyneAppCreate from './src/spyne-app-create.js';
5
+ import { createNewApp } from './src/spyne-starter-app-create.js';
6
6
  import SpyneFilePrompt from './src/spyne-file-prompt.js';
7
- const args = process.argv;
8
-
9
-
10
- //let spyneAppCreator = new SpyneAppCreator(args);
11
- //const {createAppBool} = spyneAppCreator;
12
7
 
8
+ const command = process.argv[2];
9
+ const appName = process.argv[3];
13
10
 
14
- let spyneAppCreator = new SpyneAppCreate(process.argv.slice(2));
15
- const {createAppBool} = spyneAppCreator;
16
-
17
- const startPromptFn = async()=>{
11
+ const startPromptFn = async () => {
18
12
  clear();
19
13
  SpyneCliUI.title();
20
14
  const spyneFilePrompt = new SpyneFilePrompt();
21
15
  await spyneFilePrompt.startPrompt();
22
- }
16
+ };
23
17
 
24
- if (createAppBool){
25
- //spyneAppCreator.generateResponse();
26
- spyneAppCreator.createApp();
18
+ if (command === 'new' && appName) {
19
+ // We can use an immediately-invoked async function
20
+ // OR top-level await if your Node version supports it.
21
+ (async () => {
22
+ try {
23
+ await createNewApp(appName);
24
+ } catch (err) {
25
+ console.error('Failed to create new application:', err.message);
26
+ process.exit(1);
27
+ }
28
+ })();
27
29
  } else {
28
30
  startPromptFn();
29
31
  }
30
-
package/mocha.conf.cjs ADDED
@@ -0,0 +1,16 @@
1
+ // mocha.conf.js
2
+
3
+ module.exports = {
4
+ // Look for test files in the `tests` folder
5
+ spec: 'tests/**/*.test.js',
6
+
7
+ exclude: [
8
+ 'tests/create-spyne-app-test.js'
9
+
10
+ ],
11
+ // You can also specify mocha options here
12
+ extension: ['js'], // The file extensions Mocha should look for
13
+ ui: 'bdd', // BDD-style (describe/it)
14
+ timeout: 5000, // Test timeout in milliseconds
15
+ reporter: 'spec', // The built-in "spec" reporter
16
+ };
package/package.json CHANGED
@@ -4,13 +4,13 @@
4
4
  "spyne-cli": "index.js"
5
5
  },
6
6
  "type": "module",
7
- "version": "0.5.5",
7
+ "version": "0.6.0",
8
8
  "description": "Generates spyne objects and saves them to standard spyne.",
9
9
  "main": "index.js",
10
10
  "scripts": {
11
11
  "debug": "nodemon --no-stdin index.js",
12
12
  "start": "node index.js",
13
- "test": "echo \"Error: no test specified\" && exit 1"
13
+ "test": "mocha --config mocha.conf.cjs"
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
@@ -25,24 +25,26 @@
25
25
  "dependencies": {
26
26
  "ansi-colors": "^4.1.3",
27
27
  "boxen": "^8.0.1",
28
- "chalk": "^5.3.0",
28
+ "chalk": "^5.4.1",
29
29
  "change-case": "^5.4.4",
30
30
  "clear": "^0.1.0",
31
31
  "enquirer": "^2.4.1",
32
- "figlet": "^1.7.0",
32
+ "figlet": "^1.8.0",
33
33
  "fs-extra": "^11.2.0",
34
34
  "json-stringify-safe": "^5.0.1",
35
+ "ora": "^8.1.1",
35
36
  "package-up": "^5.0.0",
36
37
  "pkg-dir": "^8.0.0",
37
38
  "pkg-up": "^5.0.0",
38
39
  "ramda": "^0.30.1",
39
40
  "read-pkg": "^9.0.1",
40
- "recast": "^0.23.9"
41
+ "recast": "^0.23.9",
42
+ "simple-git": "^3.27.0"
41
43
  },
42
44
  "devDependencies": {
43
- "chai": "^4.3.4",
44
- "mocha": "^10.7.3",
45
- "nodemon": "^3.1.7"
45
+ "chai": "^5.1.2",
46
+ "mocha": "^11.0.1",
47
+ "nodemon": "^3.1.9"
46
48
  },
47
49
  "directories": {
48
50
  "lib": "lib"
@@ -0,0 +1 @@
1
+ Some content for tests/utils/file-utils-test -- should test saving to file
@@ -4,6 +4,8 @@ import c from 'ansi-colors';
4
4
  import { exec } from 'child_process';
5
5
  import fs from 'fs-extra';
6
6
  import path from 'path';
7
+ import ora from 'ora';
8
+ import cliCursor from 'cli-cursor';
7
9
 
8
10
  class SpyneAppCreator {
9
11
  constructor(args) {
@@ -63,6 +65,9 @@ class SpyneAppCreator {
63
65
  console.log(c.green(`\nCreating a new Spyne app in ${c.cyan(appPath)} using the "${c.cyan(templateName)}" template...\n`));
64
66
 
65
67
  try {
68
+ // Hide the cursor
69
+ cliCursor.hide();
70
+
66
71
  // Clone the repository
67
72
  await this.cloneRepository(repoUrl, appFolder);
68
73
 
@@ -86,6 +91,9 @@ class SpyneAppCreator {
86
91
  this.printSuccessMessage(appFolder, appPath, skipInstall);
87
92
  } catch (error) {
88
93
  console.error(c.red(`\nError: ${error.message}`));
94
+ } finally {
95
+ // Show the cursor again
96
+ cliCursor.show();
89
97
  }
90
98
  }
91
99
 
@@ -101,33 +109,38 @@ class SpyneAppCreator {
101
109
  return templates[templateName];
102
110
  }
103
111
 
104
- // Method to clone the repository
112
+ // Method to clone the repository with spinner
105
113
  cloneRepository(repoUrl, appFolder) {
106
114
  return new Promise((resolve, reject) => {
115
+ const spinner = ora(c.green('Cloning repository...')).start();
116
+
107
117
  exec(`git clone --depth=1 ${repoUrl} ${appFolder}`, (error) => {
108
118
  if (error) {
119
+ spinner.fail(c.red('Failed to clone repository.'));
109
120
  reject(new Error(`Failed to clone repository: ${error.message}`));
110
121
  } else {
111
- console.log(c.green('Repository cloned successfully.'));
122
+ spinner.succeed(c.green('Repository cloned successfully.'));
112
123
  resolve();
113
124
  }
114
125
  });
115
126
  });
116
127
  }
117
128
 
118
- // Method to initialize a new Git repository
129
+ // Method to initialize a new Git repository with spinner
119
130
  initGitRepo(appFolder) {
120
131
  return new Promise((resolve) => {
132
+ const spinner = ora(c.green('Initializing Git repository...')).start();
133
+
121
134
  exec('git --version', (gitError) => {
122
135
  if (gitError) {
123
- console.warn(c.yellow('Git is not installed. Skipping Git initialization.'));
136
+ spinner.warn(c.yellow('Git is not installed. Skipping Git initialization.'));
124
137
  resolve();
125
138
  } else {
126
139
  exec('git init', { cwd: appFolder }, (initError) => {
127
140
  if (initError) {
128
- console.warn(c.yellow('Failed to initialize Git repository.'));
141
+ spinner.fail(c.red('Failed to initialize Git repository.'));
129
142
  } else {
130
- console.log(c.green('Git repository initialized.'));
143
+ spinner.succeed(c.green('Git repository initialized.'));
131
144
  }
132
145
  resolve();
133
146
  });
@@ -136,36 +149,42 @@ class SpyneAppCreator {
136
149
  });
137
150
  }
138
151
 
139
- // Method to install dependencies
152
+ // Method to install dependencies with spinner
140
153
  installDependencies(appFolder) {
141
- return new Promise((resolve, reject) => {
142
- console.log(c.green('\nInstalling dependencies...\n'));
154
+ return new Promise((resolve) => {
155
+ const spinner = ora(c.green('Installing dependencies...')).start();
156
+
143
157
  exec('npm install', { cwd: appFolder }, (installError) => {
144
158
  if (installError) {
145
- console.warn(c.yellow('Failed to install dependencies.'));
159
+ spinner.fail(c.red('Failed to install dependencies.'));
146
160
  resolve(); // Proceed even if installation fails
147
161
  } else {
148
- console.log(c.green('Dependencies installed successfully.'));
162
+ spinner.succeed(c.green('Dependencies installed successfully.'));
149
163
  resolve();
150
164
  }
151
165
  });
152
166
  });
153
167
  }
154
168
 
155
- // Method to update package.json
169
+ // Method to update package.json with spinner
156
170
  updatePackageJson(appFolder) {
157
171
  return new Promise((resolve, reject) => {
172
+ const spinner = ora(c.green('Updating package.json...')).start();
158
173
  const pkgPath = path.join(appFolder, 'package.json');
174
+
159
175
  fs.readJson(pkgPath)
160
176
  .then((pkg) => {
161
177
  pkg.name = path.basename(appFolder);
162
178
  return fs.writeJson(pkgPath, pkg, { spaces: 2 });
163
179
  })
164
180
  .then(() => {
165
- console.log(c.green('package.json updated.'));
181
+ spinner.succeed(c.green('package.json updated.'));
166
182
  resolve();
167
183
  })
168
- .catch((err) => reject(new Error(`Failed to update package.json: ${err.message}`)));
184
+ .catch((err) => {
185
+ spinner.fail(c.red('Failed to update package.json.'));
186
+ reject(new Error(`Failed to update package.json: ${err.message}`));
187
+ });
169
188
  });
170
189
  }
171
190
 
@@ -0,0 +1,76 @@
1
+ import c from 'ansi-colors';
2
+ import simpleGit from 'simple-git';
3
+ import { spawn } from 'child_process';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import ora from 'ora';
7
+
8
+ async function installDependencies(appName) {
9
+ const spinner = ora({
10
+ text: c.cyan('Installing dependencies...'),
11
+ spinner: 'dots'
12
+ }).start();
13
+
14
+ return new Promise((resolve, reject) => {
15
+ // Use --silent to suppress npm logs
16
+ const child = spawn('npm', ['install', '--silent'], {
17
+ cwd: appName,
18
+ // stdio: 'ignore' → all output is ignored, the spinner continues unblocked
19
+ stdio: 'ignore',
20
+ });
21
+
22
+ child.on('close', (code) => {
23
+ if (code === 0) {
24
+ spinner.succeed(c.greenBright('Dependencies installed successfully!'));
25
+ resolve();
26
+ } else {
27
+ spinner.fail(c.red(`Failed to install dependencies (exit code: ${code}).`));
28
+ reject(new Error('npm install failed'));
29
+ }
30
+ });
31
+ });
32
+ }
33
+
34
+ export async function createNewApp(appName) {
35
+ const targetDir = path.resolve(process.cwd(), appName);
36
+ const repoUrl = 'https://github.com/nybatista/starter-app-alpha.git';
37
+ const git = simpleGit();
38
+
39
+ console.log(c.cyan(`\nCreating a new Spyne app in ${c.bold(appName)}...`));
40
+
41
+ // 1) Clone repository with Ora spinner
42
+ let spinner = ora({
43
+ text: c.cyan('Cloning starter-app repository...'),
44
+ spinner: 'dots',
45
+ }).start();
46
+
47
+ try {
48
+ await git.clone(repoUrl, targetDir, ['--depth=1']);
49
+ spinner.succeed(c.greenBright('Starter App cloned successfully!'));
50
+ } catch (err) {
51
+ spinner.fail(c.red(`Failed to clone the repository: ${err.message}`));
52
+ process.exit(1);
53
+ }
54
+
55
+ // 2) Remove .git (silent - no user message)
56
+ try {
57
+ fs.rmSync(path.join(targetDir, '.git'), { recursive: true, force: true });
58
+ } catch (err) {
59
+ console.error(c.red(`Failed to remove .git folder: ${err.message}`));
60
+ process.exit(1);
61
+ }
62
+
63
+ // 3) Asynchronous install (silent)
64
+ try {
65
+ await installDependencies(appName);
66
+ } catch (err) {
67
+ console.error(c.red(err.message));
68
+ process.exit(1);
69
+ }
70
+
71
+ // 4) Final success message
72
+ console.log(`\n${c.greenBright('Success!')}`);
73
+ console.log(c.cyan(`Created ${c.bold(appName)} at ${targetDir}\n`));
74
+ console.log(c.greenBright('Next steps:'));
75
+ console.log(c.bgCyan(c.black(` cd ${appName} && npm start`)));
76
+ }
@@ -73,11 +73,11 @@ const outputMessage = ()=>{
73
73
 
74
74
  let messageOutput = '';
75
75
 
76
- const defaultStr = colors.bgBlue(colors.black(`Successfully saved the ${fileType} file.`));
76
+ const defaultStr = colors.bgYellow(colors.black(`Successfully saved the ${fileType} file.`));
77
77
 
78
78
  const checkForChannelRegisteredMsg = ()=>{
79
79
  if (_channelHasRegistered === true){
80
- return colors.bgBlue(colors.black(`Channel registered and file saved successfully.`));
80
+ return colors.bgYellow(colors.black(`Channel registered and file saved successfully.`));
81
81
  }
82
82
  return `${defaultStr}\n${createChannelRegisterSnippet()}`;
83
83
  }
package/src/ui.js CHANGED
@@ -5,7 +5,7 @@ export class SpyneCliUI {
5
5
 
6
6
  constructor() {}
7
7
  static title(){
8
- const figletTxt = figlet.textSync('spyne-cli 5.0', {
8
+ const figletTxt = figlet.textSync('spyne-cli 6.0', {
9
9
  horizontalLayout: 'universal smushing'
10
10
  })
11
11
  const chalkOutput = chalk.blue(figletTxt);
@@ -1,5 +1,4 @@
1
- import chai from 'chai';
2
- const {expect} = chai;
1
+ import {expect} from 'chai';
3
2
  import path from 'path';
4
3
 
5
4
  import SpyneAppCreator from '../src/spyne-app-creator.js';
@@ -61,4 +60,4 @@ describe('should test app creation methods', () => {
61
60
 
62
61
 
63
62
 
64
- });
63
+ });
@@ -1,7 +1,5 @@
1
1
  import SpyneFilePrompt from '../src/spyne-file-prompt.js';
2
- import chai from 'chai';
3
- const {expect, assert} = chai;
4
-
2
+ import {expect, assert} from 'chai';
5
3
 
6
4
 
7
5
  describe('should test spyne file prompt', () => {
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import GenerateFileString from '../src/templates/generate-file-string.js';
1
+ import {expect, assert} from 'chai';import GenerateFileString from '../src/templates/generate-file-string.js';
4
2
 
5
3
  describe('should test file string generator', () => {
6
4
 
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import PromptInputField from '../src/templates/generate-prompt-input-fields.js';
1
+ import {expect, assert} from 'chai';import PromptInputField from '../src/templates/generate-prompt-input-fields.js';
4
2
  import * as changeCase from "change-case";
5
3
 
6
4
  const {
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import PromptInputField from '../src/templates/generate-prompt-input-fields.js';
1
+ import {expect, assert} from 'chai';import PromptInputField from '../src/templates/generate-prompt-input-fields.js';
4
2
 
5
3
  import {MockData} from './mocks/enquirer-data.js';
6
4
 
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import GeneratePromptInputObject from '../src/templates/generate-prompt-input-object.js';
1
+ import {expect, assert} from 'chai';import GeneratePromptInputObject from '../src/templates/generate-prompt-input-object.js';
4
2
  import {Data} from '../src/spyne-template-prompts.js';
5
3
  import {MockData} from './mocks/enquirer-data.js';
6
4
  const {promptTypes} = Data;
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import {MockData} from './mocks/enquirer-data.js';
1
+ import {expect, assert} from 'chai';import {MockData} from './mocks/enquirer-data.js';
4
2
  import {AnswersData} from './mocks/answers.js';
5
3
  const {answersArr} = AnswersData;
6
4
  import {generatePromptOutput} from '../src/templates/generate-prompt-output.js';
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
-
1
+ import {expect, assert} from 'chai';
4
2
  describe('should test cli methods', () => {
5
3
 
6
4
  it('should run cli tests', () => {
@@ -1,6 +1,4 @@
1
- import chai from 'chai';
2
- const {expect, assert} = chai;
3
- import {MockData} from '../mocks/enquirer-data.js';
1
+ import {expect, assert} from 'chai';import {MockData} from '../mocks/enquirer-data.js';
4
2
  import {AnswersData} from '../mocks/answers.js';
5
3
  const {answersArr} = AnswersData;
6
4
  import * as R from 'ramda';
@@ -12,7 +10,7 @@ import path from 'path';
12
10
  import {onSaveSpyneFileToDir, copyDirSync, removeDir} from '../../src/utils/file-utils.js';
13
11
 
14
12
 
15
- const getAnswersByFileType = (fileType) => R.compose(R.head, R.filter(R.propEq('fileType', fileType)))(answersArr)
13
+ const getAnswersByFileType = (fileType) => R.compose(R.head, R.filter(R.propEq(fileType, 'fileType')))(answersArr)
16
14
 
17
15
  const defaultStr = `
18
16
  import {Subject} from 'rxjs';