spyne-cli 0.5.5 → 0.6.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/index.js +18 -17
- package/mocha.conf.cjs +16 -0
- package/package.json +10 -9
- package/src/app/channels/channel-custom.js +1 -0
- package/src/spyne-app-create.js +33 -14
- package/src/spyne-file-prompt.js +3 -1
- package/src/spyne-starter-app-create.js +81 -0
- package/src/templates/generate-file-string.js +29 -55
- package/src/templates/generate-prompt-output.js +2 -2
- package/src/ui.js +1 -1
- package/src/utils/insert-channel-strings-to-index-file.js +119 -0
- package/tests/create-spyne-app.test.js +2 -3
- package/tests/generate-file-prompt.test.js +1 -3
- package/tests/generate-file-string.test.js +1 -3
- package/tests/generate-prompt-input-fields-methods.test.js +1 -3
- package/tests/generate-prompt-input-fields.test.js +1 -3
- package/tests/generate-prompt-input-object.test.js +1 -3
- package/tests/generate-prompt-output.test.js +1 -3
- package/tests/index.test.js +1 -3
- package/tests/utils/file-utils.test.js +2 -4
package/index.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { SpyneCliUI } from './src/ui.js';
|
|
3
4
|
import clear from 'clear';
|
|
4
|
-
import
|
|
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
|
-
|
|
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 (
|
|
25
|
-
//
|
|
26
|
-
|
|
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.
|
|
7
|
+
"version": "0.6.2",
|
|
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": "
|
|
13
|
+
"test": "mocha --config mocha.conf.cjs"
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
@@ -25,24 +25,25 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"ansi-colors": "^4.1.3",
|
|
27
27
|
"boxen": "^8.0.1",
|
|
28
|
-
"chalk": "^5.
|
|
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.
|
|
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
|
-
"pkg-up": "^5.0.0",
|
|
38
38
|
"ramda": "^0.30.1",
|
|
39
39
|
"read-pkg": "^9.0.1",
|
|
40
|
-
"recast": "^0.23.9"
|
|
40
|
+
"recast": "^0.23.9",
|
|
41
|
+
"simple-git": "^3.27.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"chai": "^
|
|
44
|
-
"mocha": "^
|
|
45
|
-
"nodemon": "^3.1.
|
|
44
|
+
"chai": "^5.1.2",
|
|
45
|
+
"mocha": "^11.0.1",
|
|
46
|
+
"nodemon": "^3.1.9"
|
|
46
47
|
},
|
|
47
48
|
"directories": {
|
|
48
49
|
"lib": "lib"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Some content for tests/utils/file-utils-test -- should test saving to file
|
package/src/spyne-app-create.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
+
spinner.fail(c.red('Failed to initialize Git repository.'));
|
|
129
142
|
} else {
|
|
130
|
-
|
|
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
|
|
142
|
-
|
|
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
|
-
|
|
159
|
+
spinner.fail(c.red('Failed to install dependencies.'));
|
|
146
160
|
resolve(); // Proceed even if installation fails
|
|
147
161
|
} else {
|
|
148
|
-
|
|
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
|
-
|
|
181
|
+
spinner.succeed(c.green('package.json updated.'));
|
|
166
182
|
resolve();
|
|
167
183
|
})
|
|
168
|
-
.catch((err) =>
|
|
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
|
|
package/src/spyne-file-prompt.js
CHANGED
|
@@ -8,6 +8,7 @@ import GenerateFileString from './templates/generate-file-string.js';
|
|
|
8
8
|
import {onSaveSpyneFileToDir} from './utils/file-utils.js';
|
|
9
9
|
import {generatePromptOutput} from './templates/generate-prompt-output.js';
|
|
10
10
|
import {addChannelToIndexJS} from './utils/add-channel-to-index-file.js';
|
|
11
|
+
import {insertChannelStrings} from './utils/insert-channel-strings-to-index-file.js';
|
|
11
12
|
|
|
12
13
|
export default class SpyneFilePrompt {
|
|
13
14
|
|
|
@@ -46,7 +47,8 @@ export default class SpyneFilePrompt {
|
|
|
46
47
|
let channelHasRegistered = false;
|
|
47
48
|
|
|
48
49
|
if (fileType === 'Channel'){
|
|
49
|
-
const savedChannelToIndexProps = addChannelToIndexJS(className, fileName);
|
|
50
|
+
//const savedChannelToIndexProps = addChannelToIndexJS(className, fileName);
|
|
51
|
+
const savedChannelToIndexProps = insertChannelStrings(className, fileName);
|
|
50
52
|
|
|
51
53
|
// check if channel has been registered
|
|
52
54
|
channelHasRegistered = savedChannelToIndexProps.fileHasSaved;
|
|
@@ -0,0 +1,81 @@
|
|
|
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, [
|
|
49
|
+
'--branch=main',
|
|
50
|
+
'--single-branch',
|
|
51
|
+
'--depth=1'
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
spinner.succeed(c.greenBright('Starter App cloned successfully!'));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
spinner.fail(c.red(`Failed to clone the repository: ${err.message}`));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2) Remove .git (silent - no user message)
|
|
61
|
+
try {
|
|
62
|
+
fs.rmSync(path.join(targetDir, '.git'), { recursive: true, force: true });
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error(c.red(`Failed to remove .git folder: ${err.message}`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3) Asynchronous install (silent)
|
|
69
|
+
try {
|
|
70
|
+
await installDependencies(appName);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error(c.red(err.message));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 4) Final success message
|
|
77
|
+
console.log(`\n${c.greenBright('Success!')}`);
|
|
78
|
+
console.log(c.cyan(`Created ${c.bold(appName)} at ${targetDir}\n`));
|
|
79
|
+
console.log(c.greenBright('Next steps:'));
|
|
80
|
+
console.log(c.bgCyan(c.black(` cd ${appName} && npm start`)));
|
|
81
|
+
}
|
|
@@ -1,96 +1,70 @@
|
|
|
1
1
|
const _viewStreamTemplate = (props)=> {
|
|
2
|
-
return `import {ViewStream} from 'spyne';
|
|
2
|
+
return `import { ViewStream } from 'spyne';
|
|
3
3
|
|
|
4
4
|
export class ${props.className} extends ViewStream {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// return nested array(s)
|
|
13
|
-
return [];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
broadcastEvents() {
|
|
17
|
-
// return nested array(s)
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
onRendered() {
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
}
|
|
5
|
+
constructor(props = {}) {
|
|
6
|
+
super(props);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
addActionListeners() {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
26
12
|
|
|
13
|
+
broadcastEvents() {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
onRendered() {}
|
|
18
|
+
}
|
|
27
19
|
`
|
|
28
20
|
}
|
|
29
21
|
|
|
30
22
|
const _domElementTemplate = (props) => {
|
|
31
|
-
return `import {DomElement} from 'spyne';
|
|
32
|
-
|
|
23
|
+
return `import { DomElement } from 'spyne';
|
|
24
|
+
|
|
33
25
|
export class ${props.className} extends DomElement {
|
|
34
|
-
|
|
35
|
-
constructor(props={}) {
|
|
36
|
-
|
|
26
|
+
constructor(props = {}) {
|
|
37
27
|
super(props);
|
|
38
28
|
}
|
|
39
|
-
|
|
40
29
|
}
|
|
41
|
-
|
|
42
30
|
`
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
|
|
46
34
|
const _channelTemplate = (props)=> {
|
|
47
|
-
return `import {
|
|
48
|
-
import {Channel} from 'spyne';
|
|
49
|
-
|
|
50
|
-
export class ${props.className} extends Channel{
|
|
35
|
+
return `import { Channel } from 'spyne';
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
export class ${props.className} extends Channel {
|
|
38
|
+
constructor(name, props = {}) {
|
|
39
|
+
name = '${props.channelName}';
|
|
54
40
|
props.sendCachedPayload = ${props.replayLastPayload};
|
|
55
|
-
|
|
56
41
|
super(name, props);
|
|
57
42
|
}
|
|
58
43
|
|
|
59
|
-
onRegistered(){
|
|
60
|
-
|
|
61
|
-
}
|
|
44
|
+
onRegistered() {}
|
|
62
45
|
|
|
63
46
|
addRegisteredActions() {
|
|
64
|
-
|
|
65
47
|
return [];
|
|
66
48
|
}
|
|
67
49
|
|
|
68
|
-
onViewStreamInfo(
|
|
69
|
-
let data = obj.props();
|
|
70
|
-
}
|
|
71
|
-
|
|
50
|
+
onViewStreamInfo() {}
|
|
72
51
|
}
|
|
73
|
-
|
|
74
52
|
`
|
|
75
53
|
}
|
|
76
54
|
|
|
77
55
|
const _spyneTraitTemplate = (props)=>{
|
|
78
|
-
return `import {SpyneTrait} from 'spyne';
|
|
56
|
+
return `import { SpyneTrait } from 'spyne';
|
|
79
57
|
|
|
80
58
|
export class ${props.className} extends SpyneTrait {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
let traitPrefix = "${props.methodPrefix}";
|
|
84
|
-
|
|
59
|
+
constructor(context) {
|
|
60
|
+
let traitPrefix = '${props.methodPrefix}';
|
|
85
61
|
super(context, traitPrefix);
|
|
86
62
|
}
|
|
87
|
-
|
|
88
|
-
static ${props.methodPrefix}HelloWorld(){
|
|
89
|
-
return
|
|
63
|
+
|
|
64
|
+
static ${props.methodPrefix}HelloWorld() {
|
|
65
|
+
return 'Hello World';
|
|
90
66
|
}
|
|
91
|
-
|
|
92
67
|
}
|
|
93
|
-
|
|
94
68
|
`
|
|
95
69
|
}
|
|
96
70
|
|
|
@@ -73,11 +73,11 @@ const outputMessage = ()=>{
|
|
|
73
73
|
|
|
74
74
|
let messageOutput = '';
|
|
75
75
|
|
|
76
|
-
const defaultStr = colors.
|
|
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.
|
|
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
|
|
8
|
+
const figletTxt = figlet.textSync('spyne-cli 6.2', {
|
|
9
9
|
horizontalLayout: 'universal smushing'
|
|
10
10
|
})
|
|
11
11
|
const chalkOutput = chalk.blue(figletTxt);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import c from 'ansi-colors';
|
|
4
|
+
//import {Data} from '../spyne-template-prompts.js';
|
|
5
|
+
//const {appPath} = Data;
|
|
6
|
+
|
|
7
|
+
export function insertChannelStrings(channelClassName, channelFileName, indexJsPath="./src/index.js" ) {
|
|
8
|
+
//console.log("INFO IS ",{appPath, channelClassName, channelFileName, indexJsPath})
|
|
9
|
+
try {
|
|
10
|
+
if (!fs.existsSync(indexJsPath)) {
|
|
11
|
+
console.log(c.red(`Error: ./src/index.js not found at ${indexJsPath}`));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let fileContent = fs.readFileSync(indexJsPath, 'utf-8');
|
|
16
|
+
let lines = fileContent.split('\n');
|
|
17
|
+
|
|
18
|
+
// 1) Find the last import line
|
|
19
|
+
let lastImportIndex = -1;
|
|
20
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21
|
+
// A naive check: line starts with "import "
|
|
22
|
+
// or you can also check .match(/^import\s+/)
|
|
23
|
+
if (lines[i].trim().startsWith('import ')) {
|
|
24
|
+
lastImportIndex = i;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2) Find the line with SpyneApp.init(...) or spyneApp.init(...)
|
|
29
|
+
// We'll store the first occurrence
|
|
30
|
+
let initIndex = -1;
|
|
31
|
+
const initRegex = /(SpyneApp\.init\s*\()|(spyneApp\.init\s*\()/;
|
|
32
|
+
for (let i = 0; i < lines.length; i++) {
|
|
33
|
+
if (initRegex.test(lines[i])) {
|
|
34
|
+
initIndex = i;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 3) Build the import snippet
|
|
40
|
+
// e.g. "import {ChannelTest_1} from 'channels/channel-test-1.js';"
|
|
41
|
+
const importSnippet = `import { ${channelClassName} } from 'channels/${channelFileName}';`;
|
|
42
|
+
|
|
43
|
+
// 4) Decide where to insert the import line
|
|
44
|
+
// - Must be AFTER the last import
|
|
45
|
+
// - Must be BEFORE the init line (if init line is after the last import)
|
|
46
|
+
// If init line doesn't exist or it's above the last import, we'll just place after last import.
|
|
47
|
+
let importInsertionIndex = lastImportIndex + 1;
|
|
48
|
+
if (initIndex !== -1 && initIndex > lastImportIndex) {
|
|
49
|
+
// If there's space to place it exactly before the init line
|
|
50
|
+
// we'll insert at the init line index. That effectively
|
|
51
|
+
// pushes the init line down by 1 line.
|
|
52
|
+
importInsertionIndex = Math.min(initIndex, lastImportIndex + 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Insert the import line
|
|
56
|
+
lines.splice(importInsertionIndex, 0, importSnippet);
|
|
57
|
+
|
|
58
|
+
// 5) Build the registerChannel snippet
|
|
59
|
+
// e.g. "SpyneApp.registerChannel(new ChannelTest_1());"
|
|
60
|
+
const registerSnippet = `SpyneApp.registerChannel(new ${channelClassName}());`;
|
|
61
|
+
|
|
62
|
+
// 6) Insert the register snippet right AFTER the init line, if found
|
|
63
|
+
if (initIndex !== -1) {
|
|
64
|
+
// Because we inserted one line before the init,
|
|
65
|
+
// the init line is now at initIndex+1, so the place to insert
|
|
66
|
+
// is initIndex+2. But let's re-scan for the init line to be safe,
|
|
67
|
+
// or simply adjust.
|
|
68
|
+
let updatedInitIndex = -1;
|
|
69
|
+
for (let i = 0; i < lines.length; i++) {
|
|
70
|
+
if (initRegex.test(lines[i])) {
|
|
71
|
+
updatedInitIndex = i;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (updatedInitIndex !== -1) {
|
|
77
|
+
lines.splice(updatedInitIndex + 1, 0, registerSnippet);
|
|
78
|
+
} else {
|
|
79
|
+
// If we somehow lost the init line, fallback to end
|
|
80
|
+
lines.push(registerSnippet);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
// No init line found, let's just push it at the end or skip
|
|
84
|
+
console.log(c.yellow('Warning: SpyneApp.init(...) not found. Skipping registerChannel insertion.'));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 7) Write the updated content back
|
|
88
|
+
fs.writeFileSync(indexJsPath, lines.join('\n'), 'utf-8');
|
|
89
|
+
//console.log(c.green(`✔ Successfully updated ./src/index.js to import and register "${channelClassName}".`));
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
fileHasSaved: true,
|
|
93
|
+
errorType: null
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
} catch (error) {
|
|
98
|
+
let errorType = 'UnknownError';
|
|
99
|
+
if (error.code === 'ENOENT') {
|
|
100
|
+
errorType = 'FileNotFound';
|
|
101
|
+
} else if (error.name === 'SyntaxError') {
|
|
102
|
+
errorType = 'SyntaxError';
|
|
103
|
+
} else if (error.name === 'TypeError') {
|
|
104
|
+
errorType = 'TypeError';
|
|
105
|
+
} else if (error.name === 'PermissionError') {
|
|
106
|
+
errorType = 'PermissionError';
|
|
107
|
+
}
|
|
108
|
+
//console.warn(`spyne-cli register channel, ${errorType} incomplete `,error)
|
|
109
|
+
|
|
110
|
+
// Fail silently and return error information
|
|
111
|
+
return {
|
|
112
|
+
fileHasSaved: false,
|
|
113
|
+
errorType
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
//console.error(c.red(`Error updating index.js: ${err.message}`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
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,6 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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';
|
package/tests/index.test.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
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(
|
|
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';
|