spyne-cli 0.6.8 → 0.7.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.
- package/LICENSE +661 -0
- package/README.md +80 -30
- package/index.js +131 -21
- package/package.json +31 -6
- package/src/cli/args.js +197 -0
- package/src/cli/render.js +198 -0
- package/src/core/create-app.js +256 -0
- package/src/core/generate-app.js +155 -0
- package/src/core/generate-module.js +146 -0
- package/src/registry.js +180 -0
- package/src/spyne-file-prompt.js +77 -30
- package/src/templates/generate-prompt-input-fields.js +1 -1
- package/src/ui.js +5 -1
- package/lib/ansi.js +0 -116
- package/lib/combos.js +0 -75
- package/lib/completer.js +0 -52
- package/lib/interpolate.js +0 -266
- package/lib/keypress.js +0 -243
- package/lib/placeholder.js +0 -63
- package/lib/prompt.js +0 -485
- package/lib/prompts/autocomplete.js +0 -113
- package/lib/prompts/basicauth.js +0 -41
- package/lib/prompts/confirm.js +0 -13
- package/lib/prompts/editable.js +0 -136
- package/lib/prompts/form.js +0 -196
- package/lib/prompts/index.js +0 -28
- package/lib/prompts/input.js +0 -55
- package/lib/prompts/invisible.js +0 -11
- package/lib/prompts/list.js +0 -36
- package/lib/prompts/multiselect.js +0 -11
- package/lib/prompts/numeral.js +0 -1
- package/lib/prompts/password.js +0 -18
- package/lib/prompts/quiz.js +0 -37
- package/lib/prompts/scale.js +0 -237
- package/lib/prompts/select.js +0 -139
- package/lib/prompts/snippet.js +0 -185
- package/lib/prompts/sort.js +0 -37
- package/lib/prompts/survey.js +0 -163
- package/lib/prompts/text.js +0 -1
- package/lib/prompts/toggle.js +0 -109
- package/lib/render.js +0 -33
- package/lib/roles.js +0 -46
- package/lib/state.js +0 -69
- package/lib/styles.js +0 -144
- package/lib/symbols.js +0 -66
- package/lib/theme.js +0 -11
- package/lib/timer.js +0 -38
- package/lib/types/array.js +0 -658
- package/lib/types/auth.js +0 -29
- package/lib/types/boolean.js +0 -88
- package/lib/types/index.js +0 -7
- package/lib/types/number.js +0 -86
- package/lib/types/string.js +0 -185
- package/lib/utils.js +0 -268
- package/mocha.conf.cjs +0 -16
- package/src/app/channels/.gitkeep +0 -0
- package/src/app/channels/channel-custom.js +0 -1
- package/src/app/components/.gitkeep +0 -0
- package/src/app/traits/.gitkeep +0 -0
- package/src/hello-world-app-source/README.md +0 -15
- package/src/hello-world-app-source/apache-htaccess +0 -20
- package/src/hello-world-app-source/karma.conf.js +0 -75
- package/src/hello-world-app-source/package.json +0 -50
- package/src/hello-world-app-source/src/app/app-view.js +0 -38
- package/src/hello-world-app-source/src/app/channels/.gitkeep +0 -0
- package/src/hello-world-app-source/src/app/components/.gitkeep +0 -0
- package/src/hello-world-app-source/src/app/traits/.gitkeep +0 -0
- package/src/hello-world-app-source/src/index.js +0 -16
- package/src/hello-world-app-source/src/index.tmpl.html +0 -12
- package/src/hello-world-app-source/src/scss/01-vendors/normalize.css +0 -349
- package/src/hello-world-app-source/src/scss/02-variables/mixins.scss +0 -0
- package/src/hello-world-app-source/src/scss/03-components/.gitkeep +0 -0
- package/src/hello-world-app-source/src/scss/main.scss +0 -15
- package/src/hello-world-app-source/src/static/data/.gitkeep +0 -0
- package/src/hello-world-app-source/src/static/fonts/.gitkeep +0 -0
- package/src/hello-world-app-source/src/static/imgs/.gitkeep +0 -0
- package/src/hello-world-app-source/src/tests/unit-tests/index.test.js +0 -10
- package/src/hello-world-app-source/webpack.config.js +0 -178
- package/src/spyne-app-create.js +0 -209
- package/src/spyne-app-creator.js +0 -119
- package/src/spyne-starter-app-create.js +0 -147
- package/tests/create-spyne-app.test.js +0 -63
- package/tests/generate-file-prompt.test.js +0 -45
- package/tests/generate-file-string.test.js +0 -66
- package/tests/generate-prompt-input-fields-methods.test.js +0 -177
- package/tests/generate-prompt-input-fields.test.js +0 -179
- package/tests/generate-prompt-input-object.test.js +0 -39
- package/tests/generate-prompt-output.test.js +0 -58
- package/tests/index.test.js +0 -11
- package/tests/mocks/answers.js +0 -33
- package/tests/mocks/answers.json +0 -33
- package/tests/mocks/enquirer-data.js +0 -411
- package/tests/mocks/enquirer-data.json +0 -409
- 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
|
-
}
|
package/src/spyne-app-create.js
DELETED
|
@@ -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;
|
package/src/spyne-app-creator.js
DELETED
|
@@ -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
|
-
}
|