spyne-cli 0.6.8 → 0.7.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.
Files changed (93) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +80 -30
  3. package/index.js +123 -21
  4. package/package.json +24 -6
  5. package/src/cli/args.js +197 -0
  6. package/src/cli/render.js +184 -0
  7. package/src/core/create-app.js +232 -0
  8. package/src/core/generate-module.js +146 -0
  9. package/src/registry.js +174 -0
  10. package/src/spyne-file-prompt.js +77 -30
  11. package/src/templates/generate-prompt-input-fields.js +1 -1
  12. package/src/ui.js +5 -1
  13. package/lib/ansi.js +0 -116
  14. package/lib/combos.js +0 -75
  15. package/lib/completer.js +0 -52
  16. package/lib/interpolate.js +0 -266
  17. package/lib/keypress.js +0 -243
  18. package/lib/placeholder.js +0 -63
  19. package/lib/prompt.js +0 -485
  20. package/lib/prompts/autocomplete.js +0 -113
  21. package/lib/prompts/basicauth.js +0 -41
  22. package/lib/prompts/confirm.js +0 -13
  23. package/lib/prompts/editable.js +0 -136
  24. package/lib/prompts/form.js +0 -196
  25. package/lib/prompts/index.js +0 -28
  26. package/lib/prompts/input.js +0 -55
  27. package/lib/prompts/invisible.js +0 -11
  28. package/lib/prompts/list.js +0 -36
  29. package/lib/prompts/multiselect.js +0 -11
  30. package/lib/prompts/numeral.js +0 -1
  31. package/lib/prompts/password.js +0 -18
  32. package/lib/prompts/quiz.js +0 -37
  33. package/lib/prompts/scale.js +0 -237
  34. package/lib/prompts/select.js +0 -139
  35. package/lib/prompts/snippet.js +0 -185
  36. package/lib/prompts/sort.js +0 -37
  37. package/lib/prompts/survey.js +0 -163
  38. package/lib/prompts/text.js +0 -1
  39. package/lib/prompts/toggle.js +0 -109
  40. package/lib/render.js +0 -33
  41. package/lib/roles.js +0 -46
  42. package/lib/state.js +0 -69
  43. package/lib/styles.js +0 -144
  44. package/lib/symbols.js +0 -66
  45. package/lib/theme.js +0 -11
  46. package/lib/timer.js +0 -38
  47. package/lib/types/array.js +0 -658
  48. package/lib/types/auth.js +0 -29
  49. package/lib/types/boolean.js +0 -88
  50. package/lib/types/index.js +0 -7
  51. package/lib/types/number.js +0 -86
  52. package/lib/types/string.js +0 -185
  53. package/lib/utils.js +0 -268
  54. package/mocha.conf.cjs +0 -16
  55. package/src/app/channels/.gitkeep +0 -0
  56. package/src/app/channels/channel-custom.js +0 -1
  57. package/src/app/components/.gitkeep +0 -0
  58. package/src/app/traits/.gitkeep +0 -0
  59. package/src/hello-world-app-source/README.md +0 -15
  60. package/src/hello-world-app-source/apache-htaccess +0 -20
  61. package/src/hello-world-app-source/karma.conf.js +0 -75
  62. package/src/hello-world-app-source/package.json +0 -50
  63. package/src/hello-world-app-source/src/app/app-view.js +0 -38
  64. package/src/hello-world-app-source/src/app/channels/.gitkeep +0 -0
  65. package/src/hello-world-app-source/src/app/components/.gitkeep +0 -0
  66. package/src/hello-world-app-source/src/app/traits/.gitkeep +0 -0
  67. package/src/hello-world-app-source/src/index.js +0 -16
  68. package/src/hello-world-app-source/src/index.tmpl.html +0 -12
  69. package/src/hello-world-app-source/src/scss/01-vendors/normalize.css +0 -349
  70. package/src/hello-world-app-source/src/scss/02-variables/mixins.scss +0 -0
  71. package/src/hello-world-app-source/src/scss/03-components/.gitkeep +0 -0
  72. package/src/hello-world-app-source/src/scss/main.scss +0 -15
  73. package/src/hello-world-app-source/src/static/data/.gitkeep +0 -0
  74. package/src/hello-world-app-source/src/static/fonts/.gitkeep +0 -0
  75. package/src/hello-world-app-source/src/static/imgs/.gitkeep +0 -0
  76. package/src/hello-world-app-source/src/tests/unit-tests/index.test.js +0 -10
  77. package/src/hello-world-app-source/webpack.config.js +0 -178
  78. package/src/spyne-app-create.js +0 -209
  79. package/src/spyne-app-creator.js +0 -119
  80. package/src/spyne-starter-app-create.js +0 -147
  81. package/tests/create-spyne-app.test.js +0 -63
  82. package/tests/generate-file-prompt.test.js +0 -45
  83. package/tests/generate-file-string.test.js +0 -66
  84. package/tests/generate-prompt-input-fields-methods.test.js +0 -177
  85. package/tests/generate-prompt-input-fields.test.js +0 -179
  86. package/tests/generate-prompt-input-object.test.js +0 -39
  87. package/tests/generate-prompt-output.test.js +0 -58
  88. package/tests/index.test.js +0 -11
  89. package/tests/mocks/answers.js +0 -33
  90. package/tests/mocks/answers.json +0 -33
  91. package/tests/mocks/enquirer-data.js +0 -411
  92. package/tests/mocks/enquirer-data.json +0 -409
  93. package/tests/utils/file-utils.test.js +0 -84
@@ -1,178 +0,0 @@
1
- const path = require('path');
2
- const webpack = require('webpack');
3
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4
- const HtmlWebpackPlugin = require('html-webpack-plugin');
5
- const CopyWebpackPlugin = require('copy-webpack-plugin');
6
-
7
- const _defaultAssetsDirName = "assets";
8
- const port = 8062;
9
-
10
- let mode;
11
- let _isProduction;
12
- let _buildType;
13
-
14
- // USE "/./" FOR ROOT DOMAIN OR "./" FOR RELATIVE DOMAIN PATHS"
15
- let _relativeRoot = "./"
16
- let _publicPath;
17
- let _assetsFolder;
18
- let _imgPath;
19
-
20
- module.exports = (env={mode:"development"})=> {
21
-
22
- mode = env.mode || 'development';
23
- _isProduction = env.build === true;
24
- _buildType = process.env.buildType;
25
- _publicPath = _isProduction ? _relativeRoot : "/";
26
- _assetsFolder = _isProduction ? `${_defaultAssetsDirName}/` : "";
27
- _imgPath = `${_publicPath}${_assetsFolder}static/imgs/`;
28
-
29
- const config = {
30
- mode,
31
-
32
- entry: {
33
- index: './src/index.js'
34
- },
35
-
36
- output: {
37
- filename: `${_assetsFolder}js/[name].js`,
38
- publicPath: _publicPath,
39
- clean: true
40
- },
41
-
42
- devtool: _isProduction ? false : 'inline-cheap-source-map',
43
-
44
- devServer: {
45
- static: {
46
- directory: 'src',
47
- },
48
- historyApiFallback: true,
49
- port
50
- },
51
-
52
- plugins: getWebpackPlugins(),
53
-
54
- optimization: {
55
- splitChunks: {
56
- cacheGroups: {
57
- common: {
58
- test: /[\\/]node_modules[\\/]/,
59
- name: "vendor",
60
- chunks: 'all',
61
- }
62
- },
63
- }
64
- },
65
-
66
- module: {
67
- rules: [
68
-
69
- {
70
- test: /\.html$/,
71
- loader: "html-loader",
72
- options: {
73
- minimize: false,
74
- esModule: false,
75
- }
76
- },
77
-
78
- {
79
- test: /\.(sa|sc|c)ss$/,
80
- use: [
81
- _isProduction !== true ? 'style-loader' : MiniCssExtractPlugin.loader,
82
- 'css-loader',
83
- {
84
- loader: 'sass-loader', options: {
85
- sourceMap: true
86
- },
87
- }
88
- ]
89
- },
90
-
91
- {
92
- test: /\.(ttf|woff|woff2)$/,
93
- type: 'asset/resource',
94
- generator: {
95
- filename: `${_assetsFolder}static/fonts/[name][ext][query]`
96
- }
97
- },
98
-
99
- {
100
- test: /\.(png|jpe?g|gif|svg)$/i,
101
- type: "asset"
102
- },
103
-
104
- {
105
- test: /\.(json)$/,
106
- type: 'javascript/auto',
107
- use: [
108
- {
109
- loader: 'file-loader',
110
- options: {
111
- name: `${_assetsFolder}static/data/[name].[ext]`
112
- },
113
- }]
114
- }
115
-
116
- ]
117
- },
118
-
119
- resolve: {
120
- alias: {
121
- plugins: path.resolve(__dirname, 'src/plugins/'),
122
- imgs: path.resolve(__dirname, 'src/static/imgs/'),
123
- fonts: path.resolve(__dirname, 'src/static/fonts/'),
124
- data: path.resolve(__dirname, '/./src/static/data/'),
125
- css: path.resolve(__dirname, 'src/css/'),
126
- core: path.resolve(__dirname, 'src/core/'),
127
- traits: path.resolve(__dirname, 'src/app/traits/'),
128
- channels: path.resolve(__dirname, 'src/app/channels/'),
129
- components: path.resolve(__dirname, 'src/app/components/'),
130
- node_modules: path.resolve(__dirname, 'node_modules/')
131
-
132
- },
133
-
134
- extensions: ['.js', '.css'],
135
- }
136
- };
137
-
138
- return config;
139
-
140
- }
141
-
142
-
143
- const getWebpackPlugins = ()=> {
144
-
145
- const miniCssPlugin = ()=> {
146
- return new MiniCssExtractPlugin({
147
- filename: `${_assetsFolder}/css/main.css`
148
- });
149
- }
150
-
151
- const definePlugin = new webpack.DefinePlugin({
152
- "IMG_PATH": JSON.stringify(_imgPath),
153
- 'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
154
- });
155
-
156
- const htmlPlugin = new HtmlWebpackPlugin({
157
- template: './src/index.tmpl.html',
158
- minify: false
159
- });
160
-
161
- const getCopyPatternsPlugin = () => {
162
- const patterns = [
163
- {from: "./src/static/imgs", to: `${_assetsFolder}static/imgs`}
164
- ]
165
-
166
- if (_buildType === 'apache') {
167
- patterns.push(
168
- {from: "./apache-htaccess", to: ".htaccess", toType: "file"})
169
- }
170
-
171
- return new CopyWebpackPlugin({patterns})
172
- }
173
-
174
- return _isProduction ?
175
- [htmlPlugin, definePlugin, miniCssPlugin, getCopyPatternsPlugin()] :
176
- [htmlPlugin, definePlugin];
177
-
178
- }
@@ -1,209 +0,0 @@
1
- // SpyneAppCreator.js
2
-
3
- import c from 'ansi-colors';
4
- import { exec } from 'child_process';
5
- import fs from 'fs-extra';
6
- import path from 'path';
7
- import ora from 'ora';
8
- import cliCursor from 'cli-cursor';
9
-
10
- class SpyneAppCreator {
11
- constructor(args) {
12
- this.args = args;
13
- this.createAppBool = this.checkCreateAppCommand();
14
- }
15
-
16
- // Method to check if 'create' command is used with 'appFolder' argument
17
- checkCreateAppCommand() {
18
- const createIndex = this.args.indexOf('create');
19
- if (createIndex !== -1 && this.args[createIndex + 1]) {
20
- this.appFolder = this.args[createIndex + 1];
21
- this.parseOptions(createIndex + 2); // Start parsing options after 'appFolder'
22
- return true;
23
- }
24
- return false;
25
- }
26
-
27
- // Method to parse additional command-line options
28
- parseOptions(startIndex) {
29
- this.options = {
30
- templateName: 'starter-app', // Default template
31
- skipInstall: false,
32
- skipGit: false,
33
- };
34
-
35
- for (let i = startIndex; i < this.args.length; i++) {
36
- const arg = this.args[i];
37
- if (arg === '--template' && this.args[i + 1]) {
38
- this.options.templateName = this.args[i + 1];
39
- i++; // Skip the next argument since it's the value of --template
40
- } else if (arg === '--no-install') {
41
- this.options.skipInstall = true;
42
- } else if (arg === '--skip-git') {
43
- this.options.skipGit = true;
44
- }
45
- }
46
- }
47
-
48
- // Getter for createAppBool
49
- get createAppBool() {
50
- return this._createAppBool;
51
- }
52
-
53
- // Setter for createAppBool
54
- set createAppBool(value) {
55
- this._createAppBool = value;
56
- }
57
-
58
- // Method to initiate app creation
59
- async createApp() {
60
- const appFolder = this.appFolder;
61
- const { templateName, skipInstall, skipGit } = this.options;
62
- const repoUrl = this.getTemplateRepoUrl(templateName);
63
- const appPath = path.resolve(process.cwd(), appFolder);
64
-
65
- console.log(c.green(`\nCreating a new Spyne app in ${c.cyan(appPath)} using the "${c.cyan(templateName)}" template...\n`));
66
-
67
- try {
68
- // Hide the cursor
69
- cliCursor.hide();
70
-
71
- // Clone the repository
72
- await this.cloneRepository(repoUrl, appFolder);
73
-
74
- // Remove the .git directory
75
- await fs.remove(path.join(appFolder, '.git'));
76
-
77
- // Initialize a new Git repository if not skipped
78
- if (!skipGit) {
79
- await this.initGitRepo(appFolder);
80
- }
81
-
82
- // Install dependencies if not skipped
83
- if (!skipInstall) {
84
- await this.installDependencies(appFolder);
85
- }
86
-
87
- // Update package.json
88
- await this.updatePackageJson(appFolder);
89
-
90
- // Provide post-installation instructions
91
- this.printSuccessMessage(appFolder, appPath, skipInstall);
92
- } catch (error) {
93
- console.error(c.red(`\nError: ${error.message}`));
94
- } finally {
95
- // Show the cursor again
96
- cliCursor.show();
97
- }
98
- }
99
-
100
- // Method to get the repository URL based on the template name
101
- getTemplateRepoUrl(templateName) {
102
- const templates = {
103
- 'starter-app': 'https://github.com/nybatista/starter-app-alpha.git',
104
- // Add more templates here if needed
105
- };
106
- if (!templates[templateName]) {
107
- throw new Error(`Template "${templateName}" not found.`);
108
- }
109
- return templates[templateName];
110
- }
111
-
112
- // Method to clone the repository with spinner
113
- cloneRepository(repoUrl, appFolder) {
114
- return new Promise((resolve, reject) => {
115
- const spinner = ora(c.green('Cloning repository...')).start();
116
-
117
- exec(`git clone --depth=1 ${repoUrl} ${appFolder}`, (error) => {
118
- if (error) {
119
- spinner.fail(c.red('Failed to clone repository.'));
120
- reject(new Error(`Failed to clone repository: ${error.message}`));
121
- } else {
122
- spinner.succeed(c.green('Repository cloned successfully.'));
123
- resolve();
124
- }
125
- });
126
- });
127
- }
128
-
129
- // Method to initialize a new Git repository with spinner
130
- initGitRepo(appFolder) {
131
- return new Promise((resolve) => {
132
- const spinner = ora(c.green('Initializing Git repository...')).start();
133
-
134
- exec('git --version', (gitError) => {
135
- if (gitError) {
136
- spinner.warn(c.yellow('Git is not installed. Skipping Git initialization.'));
137
- resolve();
138
- } else {
139
- exec('git init', { cwd: appFolder }, (initError) => {
140
- if (initError) {
141
- spinner.fail(c.red('Failed to initialize Git repository.'));
142
- } else {
143
- spinner.succeed(c.green('Git repository initialized.'));
144
- }
145
- resolve();
146
- });
147
- }
148
- });
149
- });
150
- }
151
-
152
- // Method to install dependencies with spinner
153
- installDependencies(appFolder) {
154
- return new Promise((resolve) => {
155
- const spinner = ora(c.green('Installing dependencies...')).start();
156
-
157
- exec('npm install', { cwd: appFolder }, (installError) => {
158
- if (installError) {
159
- spinner.fail(c.red('Failed to install dependencies.'));
160
- resolve(); // Proceed even if installation fails
161
- } else {
162
- spinner.succeed(c.green('Dependencies installed successfully.'));
163
- resolve();
164
- }
165
- });
166
- });
167
- }
168
-
169
- // Method to update package.json with spinner
170
- updatePackageJson(appFolder) {
171
- return new Promise((resolve, reject) => {
172
- const spinner = ora(c.green('Updating package.json...')).start();
173
- const pkgPath = path.join(appFolder, 'package.json');
174
-
175
- fs.readJson(pkgPath)
176
- .then((pkg) => {
177
- pkg.name = path.basename(appFolder);
178
- return fs.writeJson(pkgPath, pkg, { spaces: 2 });
179
- })
180
- .then(() => {
181
- spinner.succeed(c.green('package.json updated.'));
182
- resolve();
183
- })
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
- });
188
- });
189
- }
190
-
191
- // Method to print success message
192
- printSuccessMessage(appFolder, appPath, skipInstall) {
193
- console.log(c.green(`\nSuccess! Created ${c.cyan(appFolder)} at ${c.cyan(appPath)}\n`));
194
- console.log('Inside that directory, you can run several commands:\n');
195
- console.log(` ${c.cyan('npm start')}`);
196
- console.log(' Starts the development server.\n');
197
- console.log(` ${c.cyan('npm run build')}`);
198
- console.log(' Bundles the app into static files for production.\n');
199
- console.log('We suggest that you begin by typing:\n');
200
- console.log(` ${c.cyan('cd')} ${appFolder}`);
201
- if (skipInstall) {
202
- console.log(` ${c.cyan('npm install')}`);
203
- }
204
- console.log(` ${c.cyan('npm start')}\n`);
205
- console.log(c.green('Happy coding!'));
206
- }
207
- }
208
-
209
- export default SpyneAppCreator;
@@ -1,119 +0,0 @@
1
- import path from 'path';
2
- import c from 'ansi-colors';
3
- import {checkIfFolderExists, copyDirSync} from './utils/file-utils.js';
4
-
5
- let _createAppBool = false;
6
- let _folderName;
7
- let _folderPath = path.resolve();
8
- let _appDirectory;
9
- let _errorMsg;
10
-
11
-
12
-
13
-
14
- import {packageDirectory} from 'pkg-dir';
15
- const globalPath = "/usr/local/lib/node_modules/spyne-cli"
16
- const _spyneAppSrcRelPath = '/src/hello-world-app-source';
17
- let _spyneAppSrcPath = path.resolve(globalPath, _spyneAppSrcRelPath);
18
-
19
-
20
- const getSpyneSrcPath = async()=> {
21
- const localPath = await packageDirectory();
22
- const mainPath = localPath || globalPath;
23
- _spyneAppSrcPath = path.resolve(mainPath, 'src/hello-world-app-source');
24
- }
25
-
26
-
27
- export default class SpyneAppCreator {
28
-
29
- constructor(args=[]) {
30
-
31
- let {createAppBool, folderName} = SpyneAppCreator.checkArgs(args);
32
-
33
- _createAppBool = createAppBool;
34
- _folderName = folderName;
35
-
36
-
37
- this.checkForErrors = SpyneAppCreator.checkForErrors.bind(this);
38
- this.generateResponse = SpyneAppCreator.generateResponse.bind(this);
39
- this.copyDirAndReturnResponse = SpyneAppCreator.copyDirAndReturnResponse.bind(this);
40
-
41
-
42
- }
43
-
44
-
45
- static async generateResponse(){
46
- try{
47
- await getSpyneSrcPath();
48
- } catch(e){
49
- console.log('getting src path err ',e);
50
- }
51
- _errorMsg = this.checkForErrors();
52
- return _errorMsg ? console.log(_errorMsg) : this.copyDirAndReturnResponse();
53
- }
54
-
55
-
56
-
57
- static copyDirAndReturnResponse(appDir=_appDirectory){
58
- copyDirSync(_spyneAppSrcPath, appDir);
59
- console.log(SpyneAppCreator.successColors(`spyne at created at ${appDir}`));
60
- }
61
-
62
-
63
- static successColors(successStr="XXXX"){
64
- return c.blueBright(successStr);
65
- }
66
-
67
- static errorColors(errorStr="XXXX"){
68
- return c.greenBright(errorStr);
69
- }
70
-
71
- static checkForErrors(folderPath=_folderPath, folderName=_folderName){
72
-
73
- let folderNameIsMissing = folderName === undefined;
74
-
75
- if (folderNameIsMissing){
76
- return SpyneAppCreator.errorColors("Please add a folder name!");
77
- }
78
-
79
- _appDirectory = `${folderPath}/${folderName}`;
80
-
81
- let pathAlreadyExistsBool = SpyneAppCreator.checkIfDirectoryExists(_appDirectory);
82
-
83
- if (pathAlreadyExistsBool){
84
- return SpyneAppCreator.errorColors('App folder already exists!');
85
- }
86
-
87
- return undefined;
88
-
89
- }
90
-
91
- static checkIfDirectoryExists(appDir=""){
92
- return checkIfFolderExists(appDir);;
93
- }
94
-
95
-
96
-
97
- static checkArgs(args){
98
- const methodStr = args.length>=3 ? args[2] : false;
99
- const folderName = args.length>=4 ? args[3] : undefined;
100
- const createAppBool = ["create-app", "new", "create", "generate"].includes(methodStr);
101
-
102
- return {folderName, createAppBool};
103
-
104
- }
105
-
106
- get createAppBool(){
107
- return _createAppBool;
108
- }
109
-
110
- get folderName(){
111
- return _folderName;
112
- }
113
-
114
- get folderPath(){
115
- return _folderPath;
116
- }
117
-
118
-
119
- }
@@ -1,147 +0,0 @@
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
- const child = spawn('npm', ['install', '--silent'], {
16
- cwd: appName,
17
- stdio: 'ignore',
18
- });
19
-
20
- child.on('close', (code) => {
21
- if (code === 0) {
22
- spinner.succeed(c.greenBright('Dependencies installed successfully!'));
23
- resolve();
24
- } else {
25
- spinner.fail(c.red(`Failed to install dependencies (exit code: ${code}).`));
26
- reject(new Error('npm install failed'));
27
- }
28
- });
29
- });
30
- }
31
-
32
- /**
33
- * Update <title> in index.tmpl.html
34
- * @param {string} targetDir
35
- * @param {string} appName
36
- */
37
- function updateIndexTitle(targetDir, appName) {
38
- const indexFilePath = path.join(targetDir, 'src', 'index.tmpl.html');
39
- if (!fs.existsSync(indexFilePath)) return; // skip if not found
40
-
41
- let content = fs.readFileSync(indexFilePath, 'utf-8');
42
- // Replace first occurrence of <title>...</title>
43
- content = content.replace(/<title>.*<\/title>/, `<title>${appName}</title>`);
44
-
45
- fs.writeFileSync(indexFilePath, content, 'utf-8');
46
- }
47
-
48
- /**
49
- * Update "name" field in package.json
50
- * @param {string} targetDir
51
- * @param {string} appName
52
- */
53
- function updatePackageName(targetDir, appName) {
54
- const pkgPath = path.join(targetDir, 'package.json');
55
- if (!fs.existsSync(pkgPath)) return; // skip if not found
56
-
57
- const pkgData = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
58
-
59
- // sanitize: e.g. convert to lowercase, replace spaces with dashes
60
- const sanitizedName = appName
61
- .toLowerCase()
62
- .replace(/\s+/g, '-')
63
- .replace(/[^a-z0-9-_]/g, ''); // remove any non-alphanumeric (optional)
64
-
65
- pkgData.name = sanitizedName || 'my-spyne-app';
66
-
67
- fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2), 'utf-8');
68
- }
69
-
70
- /**
71
- * Update the project details (title, pkg name)
72
- */
73
- function updateProjectDetails(targetDir, appName) {
74
- updateIndexTitle(targetDir, appName);
75
- updatePackageName(targetDir, appName);
76
- }
77
-
78
- /**
79
- * Creates a new Spyne app in the directory `appName`.
80
- * If isSPA is true, clones the single-page-app template (spa-base-alpha).
81
- * Otherwise, clones the default starter-app-alpha.
82
- *
83
- * @param {string} appName - Name of the app / folder
84
- * @param {boolean} isSPA - Whether to clone the SPA repo
85
- */
86
- export async function createNewApp(appName, isSPA = false) {
87
- const targetDir = path.resolve(process.cwd(), appName);
88
-
89
- // Choose repo based on isSPA flag
90
- const repoUrl = isSPA
91
- ? 'https://github.com/nybatista/spa-base-alpha.git'
92
- : 'https://github.com/nybatista/starter-app-alpha.git';
93
-
94
- const git = simpleGit();
95
-
96
- console.log(c.cyan(`\nCreating a new Spyne app in ${c.bold(appName)}...`));
97
- console.log(c.yellow(`Using repo: ${repoUrl}`));
98
-
99
- // 1) Clone repository with Ora spinner
100
- let spinner = ora({
101
- text: c.cyan('Cloning repository...'),
102
- spinner: 'dots',
103
- }).start();
104
-
105
- try {
106
- await git.clone(repoUrl, targetDir, [
107
- '--branch=main',
108
- '--single-branch',
109
- '--depth=1'
110
- ]);
111
-
112
- spinner.succeed(c.greenBright('Repository cloned successfully!'));
113
- } catch (err) {
114
- spinner.fail(c.red(`Failed to clone the repository: ${err.message}`));
115
- process.exit(1);
116
- }
117
-
118
- // 2) Remove .git
119
- try {
120
- fs.rmSync(path.join(targetDir, '.git'), { recursive: true, force: true });
121
- } catch (err) {
122
- console.error(c.red(`Failed to remove .git folder: ${err.message}`));
123
- process.exit(1);
124
- }
125
-
126
- // 3) Update index.tmpl.html <title> and package.json name
127
- try {
128
- updateProjectDetails(targetDir, appName);
129
- } catch (err) {
130
- console.error(c.red(`Failed to update project details: ${err.message}`));
131
- // not a fatal error in most cases, but you can decide to exit if needed
132
- }
133
-
134
- // 4) Install dependencies
135
- try {
136
- await installDependencies(appName);
137
- } catch (err) {
138
- console.error(c.red(err.message));
139
- process.exit(1);
140
- }
141
-
142
- // 5) Final message
143
- console.log(`\n${c.greenBright('Success!')}`);
144
- console.log(c.cyan(`Created ${c.bold(appName)} at ${targetDir}\n`));
145
- console.log(c.greenBright('Next steps:'));
146
- console.log(c.bgCyan(c.black(` cd ${appName} && npm start`)));
147
- }