superuser.app 0.0.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/LICENSE +20 -0
- package/README.md +296 -0
- package/commands/domains/add.js +102 -0
- package/commands/domains/list.js +71 -0
- package/commands/domains/remove.js +97 -0
- package/commands/domains/verify.js +130 -0
- package/commands/g/endpoint.js +32 -0
- package/commands/g/test.js +42 -0
- package/commands/init.js +220 -0
- package/commands/login.js +123 -0
- package/commands/me.js +54 -0
- package/commands/organizations/create.js +68 -0
- package/commands/profile.js +58 -0
- package/commands/register.js +127 -0
- package/commands/run.js +194 -0
- package/commands/serve.js +35 -0
- package/commands/test.js +68 -0
- package/commands/up.js +257 -0
- package/commands/update.js +84 -0
- package/commands/version.js +31 -0
- package/helpers/constants.js +3 -0
- package/helpers/draw_box.js +38 -0
- package/helpers/draw_table.js +144 -0
- package/helpers/file_writer.js +101 -0
- package/helpers/generate/endpoint/_index.js +44 -0
- package/helpers/generate/test/_index.js +112 -0
- package/helpers/load_package.js +62 -0
- package/helpers/local_server.js +72 -0
- package/helpers/settings_manager.js +108 -0
- package/helpers/verify_packages.js +85 -0
- package/index.js +7 -0
- package/package.json +31 -0
- package/src/endpoint/functions/index.js +35 -0
- package/src/init/__.env +1 -0
- package/src/init/__.env.production +1 -0
- package/src/init/__.env.staging +1 -0
- package/src/init/__.gitignore +6 -0
- package/src/init/functions/index.js +15 -0
- package/src/init/instant.package.json +3 -0
- package/src/init/package.json +16 -0
- package/src/init/serve.instant.js +52 -0
- package/src/init/test/run.js +45 -0
- package/src/init/test/tests/_index.js +53 -0
- package/src/test/blank.mjs +16 -0
- package/src/test/endpoint.mjs +20 -0
package/commands/up.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const zlib = require('zlib');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const io = require('io');
|
|
6
|
+
const tar = require('tar-stream');
|
|
7
|
+
const { minimatch } = require('minimatch');
|
|
8
|
+
|
|
9
|
+
const { Command } = require('cmnd');
|
|
10
|
+
const colors = require('colors/safe');
|
|
11
|
+
|
|
12
|
+
const constants = require('../helpers/constants.js');
|
|
13
|
+
const SettingsManager = require('../helpers/settings_manager.js');
|
|
14
|
+
const loadPackage = require('../helpers/load_package.js');
|
|
15
|
+
const SUPPORTED_ENVS = ['development', 'staging', 'production'];
|
|
16
|
+
const DEFAULT_IGNORE = [
|
|
17
|
+
'node_modules/', // modules installed on deploy
|
|
18
|
+
'test/', // tests not relevent for deploy
|
|
19
|
+
'.git', // do not deploy git history
|
|
20
|
+
'.DS_Store', // do not deploy macOS files
|
|
21
|
+
'package-lock.json', // deps installed from package.json
|
|
22
|
+
'serve.instant.js' // not used by Superuser Package Registry; irrelevant
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function formatSize (size) {
|
|
26
|
+
const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
|
27
|
+
let curUnit = 0;
|
|
28
|
+
while (size > 1024) {
|
|
29
|
+
size = size / 1024;
|
|
30
|
+
curUnit++;
|
|
31
|
+
}
|
|
32
|
+
let strSize = size.toFixed(2);
|
|
33
|
+
const intSize = strSize.split('.')[0];
|
|
34
|
+
let decSize = strSize.split('.')[1];
|
|
35
|
+
while (decSize.endsWith('0')) {
|
|
36
|
+
decSize = decSize.slice(0, -1);
|
|
37
|
+
}
|
|
38
|
+
return intSize + (decSize ? `.${decSize}` : '') + (units[curUnit] || 'overflowB');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readFiles (base, properties, dir, data) {
|
|
42
|
+
|
|
43
|
+
dir = dir || '/';
|
|
44
|
+
data = data || [];
|
|
45
|
+
properties = properties || {};
|
|
46
|
+
|
|
47
|
+
let ignore = properties.ignore || [];
|
|
48
|
+
|
|
49
|
+
return fs.readdirSync(path.join(base, dir)).reduce((data, f) => {
|
|
50
|
+
|
|
51
|
+
let pathname = path.join(dir, f);
|
|
52
|
+
let fullpath = path.join(base, pathname);
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < ignore.length; i++) {
|
|
55
|
+
let filename = pathname.split(path.sep).join('/').slice(1);
|
|
56
|
+
let pattern = ignore[i];
|
|
57
|
+
if (minimatch(filename, pattern, {matchBase: true, dot: true})) {
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (fs.statSync(fullpath).isDirectory()) {
|
|
63
|
+
return readFiles(base, properties, pathname, data);
|
|
64
|
+
} else {
|
|
65
|
+
let filename = pathname[0] === path.sep ? pathname.substr(1) : pathname;
|
|
66
|
+
let buffer = fs.readFileSync(fullpath);
|
|
67
|
+
filename = filename.split(path.sep).join('/'); // Windows
|
|
68
|
+
data.push({filename: filename, buffer: buffer});
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}, data);
|
|
73
|
+
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
class UpCommand extends Command {
|
|
77
|
+
|
|
78
|
+
constructor() {
|
|
79
|
+
super('up');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
help () {
|
|
83
|
+
return {
|
|
84
|
+
description: 'Deploys your project to the Superuser Package Registry',
|
|
85
|
+
args: [],
|
|
86
|
+
flags: {
|
|
87
|
+
v: 'Verbose mode; print full details of packaging'
|
|
88
|
+
},
|
|
89
|
+
vflags: {
|
|
90
|
+
env: `Deployment environment; defaults to "development"`
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async run (params) {
|
|
96
|
+
|
|
97
|
+
const settings = SettingsManager.read(true);
|
|
98
|
+
const InstantPackage = await loadPackage(params, true);
|
|
99
|
+
|
|
100
|
+
const isVerbose = params.flags.hasOwnProperty('v');
|
|
101
|
+
const env = (params.vflags.env || [])[0] || 'development';
|
|
102
|
+
if (!SUPPORTED_ENVS.includes(env)) {
|
|
103
|
+
throw new Error(`env must be one of: "${SUPPORTED_ENVS.join('", "')}"`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let functJSON;
|
|
107
|
+
let packageJSON;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
functJSON = require(path.join(process.cwd(), 'superuser.json'));
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error(e);
|
|
113
|
+
console.error(new Error('Invalid "superuser.json" in this directory'));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// override name from CLI
|
|
118
|
+
const name = (params.flags.n || [''])[0].trim() || functJSON.name;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
packageJSON = require(path.join(process.cwd(), 'package.json'));
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(e);
|
|
124
|
+
console.error(new Error('Invalid "package.json" in this directory'));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log();
|
|
129
|
+
console.log(`Packaging "${colors.bold(name)}" (${colors.bold.grey(env)})...`);
|
|
130
|
+
console.log();
|
|
131
|
+
|
|
132
|
+
!fs.existsSync('/tmp') && fs.mkdirSync('/tmp');
|
|
133
|
+
!fs.existsSync('/tmp/ibot') && fs.mkdirSync('/tmp/ibot', 0o777);
|
|
134
|
+
const tmpName = name.replace(/\//g, '.');
|
|
135
|
+
const tmpPath = `/tmp/ibot/${tmpName}.${new Date().valueOf()}.tar.gz`;
|
|
136
|
+
|
|
137
|
+
const t0 = new Date().valueOf();
|
|
138
|
+
|
|
139
|
+
const tarball = fs.createWriteStream(tmpPath, {mode: 0o777});
|
|
140
|
+
const pack = tar.pack();
|
|
141
|
+
|
|
142
|
+
let ignore = DEFAULT_IGNORE.slice();
|
|
143
|
+
if (fs.existsSync(path.join(process.cwd(), '.ibotignore'))) {
|
|
144
|
+
ignore = ignore.concat(
|
|
145
|
+
fs.readFileSync(path.join(process.cwd(), '.ibotignore')).toString()
|
|
146
|
+
.split('\n')
|
|
147
|
+
.map(line => line.trim())
|
|
148
|
+
.filter(line => !!line && !line.trim().startsWith('#'))
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
ignore = ignore.map(v => v.endsWith('/') ? `${v}*` : v);
|
|
152
|
+
const data = readFiles(process.cwd(), {ignore: ignore});
|
|
153
|
+
|
|
154
|
+
// pipe the pack stream to your file
|
|
155
|
+
pack.pipe(tarball);
|
|
156
|
+
|
|
157
|
+
// Run everything in parallel...
|
|
158
|
+
let fileCount = 0;
|
|
159
|
+
let fileSize = 0;
|
|
160
|
+
await Promise.all(data.map(file => {
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
isVerbose && console.log(`Packaging: "${file.filename}" (${formatSize(file.buffer.byteLength)}) ...`);
|
|
163
|
+
fileSize += file.buffer.byteLength;
|
|
164
|
+
fileCount++;
|
|
165
|
+
pack.entry(
|
|
166
|
+
{name: file.filename},
|
|
167
|
+
file.buffer,
|
|
168
|
+
(err, result) => {
|
|
169
|
+
if (err) {
|
|
170
|
+
reject(err);
|
|
171
|
+
} else {
|
|
172
|
+
resolve(result);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
});
|
|
177
|
+
}));
|
|
178
|
+
isVerbose && console.log();
|
|
179
|
+
|
|
180
|
+
pack.finalize();
|
|
181
|
+
const result = await new Promise(resolve => {
|
|
182
|
+
tarball.on('close', () => {
|
|
183
|
+
const buffer = fs.readFileSync(tmpPath);
|
|
184
|
+
fs.unlinkSync(tmpPath);
|
|
185
|
+
const result = zlib.gzipSync(buffer);
|
|
186
|
+
resolve(result);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const packageSize = result.byteLength;
|
|
191
|
+
const t = new Date().valueOf() - t0;
|
|
192
|
+
console.log(`Packaged ${fileCount} files (${formatSize(fileSize)}) in ${t} ms!`);
|
|
193
|
+
console.log(`Final tarball: ${formatSize(packageSize)} (${((packageSize / fileSize) * 100).toFixed(2)}% compression ratio, lower is better)`);
|
|
194
|
+
console.log();
|
|
195
|
+
|
|
196
|
+
const host = settings.activeProfile.host || constants.BASE_URL;
|
|
197
|
+
console.log(`Deploying to ${colors.bold(host)} ...`);
|
|
198
|
+
console.log();
|
|
199
|
+
const upResult = await new Promise(async (resolve) => {
|
|
200
|
+
await io.post(
|
|
201
|
+
`${host}/v1/packages`,
|
|
202
|
+
settings.activeProfile.key,
|
|
203
|
+
null,
|
|
204
|
+
{
|
|
205
|
+
name: name,
|
|
206
|
+
environment: env,
|
|
207
|
+
timeout: Math.max(1, Math.min(parseInt(functJSON.timeout) || 0), 900),
|
|
208
|
+
tarball: {_base64: result.toString('base64')},
|
|
209
|
+
_stream: true
|
|
210
|
+
},
|
|
211
|
+
({event, data, id}) => {
|
|
212
|
+
if (event === '@response') {
|
|
213
|
+
let json;
|
|
214
|
+
try {
|
|
215
|
+
json = JSON.parse(data.body);
|
|
216
|
+
data.data = json;
|
|
217
|
+
} catch (e) {
|
|
218
|
+
// do nothing;
|
|
219
|
+
}
|
|
220
|
+
resolve(data);
|
|
221
|
+
} else if (event === 'log') {
|
|
222
|
+
console.log(`${colors.bold.grey(`Deploy:`)} ${data}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (upResult.statusCode !== 200) {
|
|
229
|
+
if (upResult.data && upResult.data.error && upResult.data.error.message) {
|
|
230
|
+
throw new Error(upResult.data.error.message);
|
|
231
|
+
} else {
|
|
232
|
+
throw new Error(`Invalid response from server: statusCode ${upResult.statusCode}`);
|
|
233
|
+
}
|
|
234
|
+
} else if (upResult.data?.packageVersions?.[0]?.packageDeployments?.[0]?.error_json) {
|
|
235
|
+
const errorJSON = upResult.data?.packageVersions[0].packageDeployments[0].error_json;
|
|
236
|
+
const error = new Error(errorJSON.message);
|
|
237
|
+
if (errorJSON.details) {
|
|
238
|
+
error.details = errorJSON.details;
|
|
239
|
+
}
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const url = upResult.data.version_urls[env];
|
|
244
|
+
const time = new Date().valueOf() - t0;
|
|
245
|
+
|
|
246
|
+
console.log();
|
|
247
|
+
console.log(`${colors.bold.green('Success:')} ${colors.bold(name)} deployed to ${colors.bold.grey(env)} in ${time} ms!`);
|
|
248
|
+
console.log(` => ${colors.bold.blue(url)}`);
|
|
249
|
+
console.log();
|
|
250
|
+
|
|
251
|
+
return void 0;
|
|
252
|
+
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = UpCommand;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const { Command } = require('cmnd');
|
|
2
|
+
|
|
3
|
+
const childProcess = require('child_process');
|
|
4
|
+
const colors = require('colors/safe');
|
|
5
|
+
const semver = require('semver');
|
|
6
|
+
|
|
7
|
+
const loadPackage = require('../helpers/load_package.js');
|
|
8
|
+
const verifyPackages = require('../helpers/verify_packages.js');
|
|
9
|
+
|
|
10
|
+
class UpdateCommand extends Command {
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super('update');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
help () {
|
|
17
|
+
return {
|
|
18
|
+
description: 'Updates all installed dependencies to latest version',
|
|
19
|
+
args: [],
|
|
20
|
+
flags: {},
|
|
21
|
+
vflags: {
|
|
22
|
+
'force-local': 'Forces a re-installation of all local dependencies',
|
|
23
|
+
'force': 'Forces a re-installation of all dependencies (includes the CLI)'
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run (params) {
|
|
29
|
+
|
|
30
|
+
console.log();
|
|
31
|
+
console.log(colors.black.bold(`Info:`) + ` Retrieving current package details...`);
|
|
32
|
+
console.log();
|
|
33
|
+
|
|
34
|
+
const verifiedPackages = await verifyPackages();
|
|
35
|
+
|
|
36
|
+
console.log(verifiedPackages);
|
|
37
|
+
|
|
38
|
+
const updatePackages = verifiedPackages.filter(pkg => pkg.latest && semver.gt(pkg.latest, pkg.version));
|
|
39
|
+
const globalPackages = (params.vflags['force'])
|
|
40
|
+
? verifiedPackages.filter(pkg => pkg.global)
|
|
41
|
+
: updatePackages.filter(pkg => pkg.global);
|
|
42
|
+
const localDevPackages = (params.vflags['force'] || params.vflags['force-local'])
|
|
43
|
+
? verifiedPackages.filter(pkg => pkg.dev)
|
|
44
|
+
: updatePackages.filter(pkg => pkg.dev);
|
|
45
|
+
const localPackages = (params.vflags['force'] || params.vflags['force-local'])
|
|
46
|
+
? verifiedPackages.filter(pkg => !pkg.global && !pkg.dev)
|
|
47
|
+
: updatePackages.filter(pkg => !pkg.global && !pkg.dev);
|
|
48
|
+
|
|
49
|
+
if (globalPackages.length) {
|
|
50
|
+
const result = childProcess.spawnSync(`npm i ${globalPackages.map(pkg => `${pkg.name}@latest`).join(' ')} -g`, {stdio: 'inherit', shell: true});
|
|
51
|
+
if (result.status !== 0) {
|
|
52
|
+
throw new Error(`Error installing global dependencies`);
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(colors.bold.green(`Installed:`) + ` ${globalPackages.length} global packages\n${globalPackages.map(pkg => ` - ${pkg.name}@${pkg.latest}`).join('\n')}`);
|
|
56
|
+
}
|
|
57
|
+
if (localDevPackages.length) {
|
|
58
|
+
const result = childProcess.spawnSync(`npm i ${localDevPackages.map(pkg => `${pkg.name}@latest`).join(' ')} --save-dev`, {stdio: 'inherit', shell: true});
|
|
59
|
+
if (result.status !== 0) {
|
|
60
|
+
throw new Error(`Error installing local devDependencies`);
|
|
61
|
+
}
|
|
62
|
+
console.log();
|
|
63
|
+
console.log(colors.bold.green(`Installed:`) + ` ${localPackages.length} local devDependencies\n${localPackages.map(pkg => ` - ${pkg.name}@${pkg.latest}`).join('\n')}`);
|
|
64
|
+
}
|
|
65
|
+
if (localPackages.length) {
|
|
66
|
+
const result = childProcess.spawnSync(`npm i ${localPackages.map(pkg => `${pkg.name}@latest`).join(' ')}`, {stdio: 'inherit', shell: true});
|
|
67
|
+
if (result.status !== 0) {
|
|
68
|
+
throw new Error(`Error installing local dependencies`);
|
|
69
|
+
}
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(colors.bold.green(`Installed:`) + ` ${localPackages.length} local dependencies\n${localPackages.map(pkg => ` - ${pkg.name}@${pkg.latest}`).join('\n')}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log();
|
|
75
|
+
console.log(colors.bold.green(`Up to date!`) + ` All of your ${colors.bold.green('Superuser Package')} dependencies are up to date.`);
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
return void 0;
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = UpdateCommand;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { Command } = require('cmnd');
|
|
2
|
+
|
|
3
|
+
const verifyPackages = require('../helpers/verify_packages.js');
|
|
4
|
+
|
|
5
|
+
class VersionCommand extends Command {
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
super('version');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
help () {
|
|
12
|
+
return {
|
|
13
|
+
description: 'Retrieves the current version of installed dependencies',
|
|
14
|
+
args: [],
|
|
15
|
+
flags: {},
|
|
16
|
+
vflags: {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async run (params) {
|
|
21
|
+
|
|
22
|
+
const verifiedPackages = await verifyPackages();
|
|
23
|
+
console.log(verifiedPackages);
|
|
24
|
+
|
|
25
|
+
return void 0;
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = VersionCommand;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const colors = require('colors/safe');
|
|
2
|
+
|
|
3
|
+
const stripColors = str => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
center: (color, ...text) => {
|
|
7
|
+
colorize = colors[color] || (v => v);
|
|
8
|
+
let lines = text.join('\n').split('\n')
|
|
9
|
+
.map(line => stripColors(line).length % 2 === 0 ? line : line + ' ');
|
|
10
|
+
let maxLength = Math.max.apply(Math, lines.map(line => stripColors(line).length));
|
|
11
|
+
let minPad = 2;
|
|
12
|
+
let length = maxLength + minPad * 2;
|
|
13
|
+
return [].concat(
|
|
14
|
+
colorize(`╔` + `═`.repeat(length) + `╗`),
|
|
15
|
+
lines.map(line => {
|
|
16
|
+
let count = (length - stripColors(line).length) / 2;
|
|
17
|
+
return colorize(`║`) + ` `.repeat(count) + line + ` `.repeat(count) + colorize(`║`);
|
|
18
|
+
}),
|
|
19
|
+
colorize(`╚` + `═`.repeat(length) + `╝`)
|
|
20
|
+
).join('\n');
|
|
21
|
+
},
|
|
22
|
+
left: (color, ...text) => {
|
|
23
|
+
colorize = colors[color] || (v => v);
|
|
24
|
+
let lines = text.join('\n').split('\n')
|
|
25
|
+
.map(line => stripColors(line).length % 2 === 0 ? line : line + ' ');
|
|
26
|
+
let maxLength = Math.max.apply(Math, lines.map(line => stripColors(line).length));
|
|
27
|
+
let minPad = 2;
|
|
28
|
+
let length = maxLength + minPad * 2;
|
|
29
|
+
return [].concat(
|
|
30
|
+
colorize(`╔` + `═`.repeat(length) + `╗`),
|
|
31
|
+
lines.map(line => {
|
|
32
|
+
let count = length - stripColors(line).length - minPad;
|
|
33
|
+
return colorize(`║`) + ` `.repeat(minPad) + line + ` `.repeat(count) + colorize(`║`);
|
|
34
|
+
}),
|
|
35
|
+
colorize(`╚` + `═`.repeat(length) + `╝`),
|
|
36
|
+
).join('\n');
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const colors = require('colors/safe');
|
|
3
|
+
|
|
4
|
+
const stripColors = str => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
5
|
+
|
|
6
|
+
const renderLine = (columns, rows, index, color) => {
|
|
7
|
+
const columnData = columns.map(columnName => {
|
|
8
|
+
const colLength = stripColors(columnName + '').length;
|
|
9
|
+
const maxLength = rows.reduce((max, row) => {
|
|
10
|
+
return Math.max(stripColors(row[columnName] + '').length, max);
|
|
11
|
+
}, colLength);
|
|
12
|
+
return {
|
|
13
|
+
name: columnName,
|
|
14
|
+
maxLength
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
const outlineColorize = colors.reset[color] || colors.reset;
|
|
18
|
+
const colorize = colors[color] || (v => v);
|
|
19
|
+
const minPad = 1;
|
|
20
|
+
if (index === 'top') {
|
|
21
|
+
return (
|
|
22
|
+
outlineColorize(`╒`) +
|
|
23
|
+
columnData.map(c => {
|
|
24
|
+
return outlineColorize(`═`.repeat(c.maxLength + minPad * 2))
|
|
25
|
+
}).join(outlineColorize('╤')) +
|
|
26
|
+
outlineColorize(`╕`) + '\n' +
|
|
27
|
+
outlineColorize(` │`) +
|
|
28
|
+
columnData.map(c => {
|
|
29
|
+
const item = c.name + '';
|
|
30
|
+
return Array(minPad).fill(' ').join('') +
|
|
31
|
+
item +
|
|
32
|
+
Array(c.maxLength - item.length + 1).fill(' ').join('');
|
|
33
|
+
}).join(outlineColorize('│')) +
|
|
34
|
+
outlineColorize(`│`) + '\n' +
|
|
35
|
+
outlineColorize(` ╞`) +
|
|
36
|
+
columnData.map(c => {
|
|
37
|
+
return outlineColorize(`═`.repeat(c.maxLength + minPad * 2));
|
|
38
|
+
}).join(outlineColorize('╪')) +
|
|
39
|
+
outlineColorize(`╡`)
|
|
40
|
+
);
|
|
41
|
+
} else if (index === 'bottom') {
|
|
42
|
+
return (
|
|
43
|
+
outlineColorize(`└`) +
|
|
44
|
+
columnData.map(c => {
|
|
45
|
+
return outlineColorize(`─`.repeat(c.maxLength + minPad * 2));
|
|
46
|
+
}).join(outlineColorize('┴')) +
|
|
47
|
+
outlineColorize(`┘`)
|
|
48
|
+
);
|
|
49
|
+
} else if (index === 'middle') {
|
|
50
|
+
return (
|
|
51
|
+
outlineColorize(`├`) +
|
|
52
|
+
columnData.map(c => {
|
|
53
|
+
return outlineColorize(`─`.repeat(c.maxLength + minPad * 2));
|
|
54
|
+
}).join(outlineColorize('┼')) +
|
|
55
|
+
outlineColorize(`┤`)
|
|
56
|
+
);
|
|
57
|
+
} else if (index === 'bottom-cancel') {
|
|
58
|
+
return (
|
|
59
|
+
outlineColorize(`└`) +
|
|
60
|
+
columnData.map(c => {
|
|
61
|
+
return outlineColorize(`─`.repeat(c.maxLength + minPad * 2));
|
|
62
|
+
}).join(outlineColorize('─')) +
|
|
63
|
+
outlineColorize(`┘`)
|
|
64
|
+
);
|
|
65
|
+
} else if (index === 'middle-cancel') {
|
|
66
|
+
return (
|
|
67
|
+
outlineColorize(`├`) +
|
|
68
|
+
columnData.map(c => {
|
|
69
|
+
return outlineColorize(`─`.repeat(c.maxLength + minPad * 2));
|
|
70
|
+
}).join(outlineColorize('┴')) +
|
|
71
|
+
outlineColorize(`┤`)
|
|
72
|
+
);
|
|
73
|
+
} else if (index === -1) {
|
|
74
|
+
const length = Math.max(0, columnData.reduce((sum, c) => {
|
|
75
|
+
return sum + (c.maxLength + minPad * 2 + 1); // outline
|
|
76
|
+
}, -(1 + minPad * 2))); // -2 for left / right minPad
|
|
77
|
+
const item = `${colors.bold.red('x')} cancel`;
|
|
78
|
+
return (
|
|
79
|
+
colorize(`│`) +
|
|
80
|
+
Array(minPad).fill(' ').join('') +
|
|
81
|
+
item +
|
|
82
|
+
Array(length - stripColors(item).length).fill(' ').join('') +
|
|
83
|
+
Array(minPad).fill(' ').join('') +
|
|
84
|
+
colorize(`│`)
|
|
85
|
+
);
|
|
86
|
+
} else {
|
|
87
|
+
return (
|
|
88
|
+
colorize(`│`) +
|
|
89
|
+
columnData.map(c => {
|
|
90
|
+
const item = rows[index][c.name] + '';
|
|
91
|
+
return Array(minPad).fill(' ').join('') +
|
|
92
|
+
item +
|
|
93
|
+
Array(c.maxLength - stripColors(item).length + 1).fill(' ').join('');
|
|
94
|
+
}).join(colorize('│')) +
|
|
95
|
+
colorize(`│`)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
render: (columns, rows, color = 'grey') => {
|
|
102
|
+
console.log([].concat(
|
|
103
|
+
' ' + renderLine(columns, rows, 'top', color),
|
|
104
|
+
rows.map((row, i) => {
|
|
105
|
+
return ' ' + renderLine(columns, rows, i, color);
|
|
106
|
+
}),
|
|
107
|
+
' ' + renderLine(columns, rows, 'bottom', color)
|
|
108
|
+
).join('\n'));
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
selectIndexFromTable: async (title, columns, rows, color = 'grey') => {
|
|
112
|
+
const result = await inquirer.prompt([
|
|
113
|
+
{
|
|
114
|
+
name: 'index',
|
|
115
|
+
type: 'list',
|
|
116
|
+
message: title,
|
|
117
|
+
loop: false,
|
|
118
|
+
pageSize: 100,
|
|
119
|
+
choices: [].concat(
|
|
120
|
+
new inquirer.Separator(
|
|
121
|
+
renderLine(columns, rows, 'top', color)
|
|
122
|
+
),
|
|
123
|
+
rows.map((_, i) => {
|
|
124
|
+
return {
|
|
125
|
+
name: renderLine(columns, rows, i, color),
|
|
126
|
+
value: i
|
|
127
|
+
}
|
|
128
|
+
}),
|
|
129
|
+
new inquirer.Separator(
|
|
130
|
+
renderLine(columns, rows, 'middle-cancel', color)
|
|
131
|
+
),
|
|
132
|
+
{
|
|
133
|
+
name: renderLine(columns, rows, -1, color),
|
|
134
|
+
value: -1
|
|
135
|
+
},
|
|
136
|
+
new inquirer.Separator(
|
|
137
|
+
renderLine(columns, rows, 'bottom-cancel', color)
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
return result.index;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const colors = require('colors/safe');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
|
|
7
|
+
readRecursive (root, files = {}, prefix = '', pathname = '') {
|
|
8
|
+
files = files || {};
|
|
9
|
+
let filenames = fs.readdirSync(path.join(root, pathname));
|
|
10
|
+
filenames.forEach(filename => {
|
|
11
|
+
let filepath = [pathname, filename].join('/');
|
|
12
|
+
let fullpath = path.join(root, filepath);
|
|
13
|
+
let stat = fs.statSync(fullpath);
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
this.readRecursive(root, files, prefix, filepath);
|
|
16
|
+
} else {
|
|
17
|
+
files[`${prefix}${filepath}`] = fs.readFileSync(fullpath);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return files;
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
writeFile (filename, buffer, overwrite = true, validate = false) {
|
|
24
|
+
if (filename.startsWith('/')) {
|
|
25
|
+
filename = `.${filename}`;
|
|
26
|
+
}
|
|
27
|
+
if (!filename.startsWith('.')) {
|
|
28
|
+
filename = `./${filename}`;
|
|
29
|
+
}
|
|
30
|
+
// e.g. __.gitignore becomes .gitignore
|
|
31
|
+
// this prevents unintended behavior in this repo from .env, .gitignore files and more
|
|
32
|
+
filename = filename.replaceAll('/__.', '/.');
|
|
33
|
+
console.log(colors.bold.black(`FileWriter:`) + ` Writing file "${filename}" ...`);
|
|
34
|
+
let paths = filename.split('/').slice(1, -1);
|
|
35
|
+
for (let i = 1; i <= paths.length; i++) {
|
|
36
|
+
let pathname = paths.slice(0, i).join('/');
|
|
37
|
+
if (!fs.existsSync(pathname)) {
|
|
38
|
+
fs.mkdirSync(pathname);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!overwrite) {
|
|
42
|
+
if (fs.existsSync(path.join('.', filename))) {
|
|
43
|
+
if (validate) {
|
|
44
|
+
throw new Error(`Could not write, file already exists: "${filename}"`);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(colors.bold.black(`FileWriter:`) + colors.yellow(` Warn: Skipped "${filename}" (already exists)`));
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
fs.writeFileSync(path.join('.', filename), buffer);
|
|
52
|
+
return true;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
writeLine (filename = '', line) {
|
|
56
|
+
console.log(colors.bold.black(`FileWriter:`) + ` Writing line "${line}" to "${filename}"`);
|
|
57
|
+
const exists = fs.existsSync(filename);
|
|
58
|
+
let fileString = '';
|
|
59
|
+
if (exists) {
|
|
60
|
+
fileString = fs.readFileSync(filename).toString();
|
|
61
|
+
}
|
|
62
|
+
let lines = fileString.split('\n')
|
|
63
|
+
.map(l => l.trim())
|
|
64
|
+
.filter(l => !!l);
|
|
65
|
+
if (!lines.find(l => l === line)) {
|
|
66
|
+
lines.push(line);
|
|
67
|
+
}
|
|
68
|
+
fs.writeFileSync(filename, lines.join('\n'));
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
writeJSON (filename, key, value, keepValueIfExists = false) {
|
|
72
|
+
console.log(colors.bold.black(`FileWriter:`) + ` Writing [${key}=${JSON.stringify(value)}] to JSON file "${filename}"`);
|
|
73
|
+
const exists = fs.existsSync(filename);
|
|
74
|
+
let json = {};
|
|
75
|
+
if (exists) {
|
|
76
|
+
let fileString = fs.readFileSync(filename).toString();
|
|
77
|
+
try {
|
|
78
|
+
json = JSON.parse(fileString);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
throw new Error(`Could not write, invalid JSON: "${filename}"`);
|
|
81
|
+
}
|
|
82
|
+
if (!json || typeof json !== 'object' || Array.isArray(json)) {
|
|
83
|
+
throw new Error(`Could not write, JSON not an object: "${filename}"`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let keys = key.split('.');
|
|
87
|
+
let writeKey = keys.pop();
|
|
88
|
+
let obj = json;
|
|
89
|
+
while (keys.length) {
|
|
90
|
+
let curKey = keys.shift();
|
|
91
|
+
obj[curKey] = obj[curKey] || {};
|
|
92
|
+
obj = obj[curKey];
|
|
93
|
+
}
|
|
94
|
+
if (!(writeKey in obj) || !keepValueIfExists) {
|
|
95
|
+
obj[writeKey] = value;
|
|
96
|
+
}
|
|
97
|
+
fs.writeFileSync(filename, JSON.stringify(json, null, 2));
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
};
|