@m2c2kit/cli 0.1.11 → 0.3.3
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/README.md +67 -2
- package/dist/.env +1 -1
- package/dist/assets/cli-starter/img/assessmentExample.png +0 -0
- package/dist/assets/css/bootstrap-datepicker.standalone.css +539 -0
- package/dist/assets/css/bootstrap-slider.css +328 -0
- package/dist/assets/css/defaultV2.css +3655 -0
- package/dist/assets/css/m2c2kit.css +72 -0
- package/dist/assets/css/modern.css +2564 -0
- package/dist/assets/css/nouislider.css +309 -0
- package/dist/assets/css/select2.css +712 -0
- package/dist/assets/css/survey.css +1006 -0
- package/dist/cli.js +428 -488
- package/dist/isReservedWord.js +98 -0
- package/dist/templates/README.md.handlebars +1 -1
- package/dist/templates/index.html.handlebars +26 -38
- package/dist/templates/index.ts.handlebars +579 -0
- package/dist/templates/package.json.handlebars +15 -14
- package/dist/templates/rollup.config.mjs.handlebars +71 -0
- package/dist/templates/tsconfig.json.handlebars +4 -14
- package/package.json +16 -16
- package/dist/scripts/post-install.mjs +0 -27
- package/dist/templates/rollup.config.js.handlebars +0 -97
- package/dist/templates/starter.ts.handlebars +0 -426
- /package/dist/{fonts → assets/cli-starter/fonts}/roboto/LICENSE.txt +0 -0
- /package/dist/{fonts → assets/cli-starter/fonts}/roboto/Roboto-Regular.ttf +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,488 +1,428 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import yargs from "yargs";
|
|
3
|
-
import { hideBin } from "yargs/helpers";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import handlebars from "handlebars";
|
|
7
|
-
import { dirname } from "path";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
|
-
import { spawn } from "child_process";
|
|
10
|
-
import ora from "ora";
|
|
11
|
-
import chalk from "chalk";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
npm = spawn(`npm`, ["
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
npm = spawn(`npm`, ["install"], { cwd: newFolderPath });
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
console.log(
|
|
192
|
-
console.log(
|
|
193
|
-
console.log("
|
|
194
|
-
console.log(
|
|
195
|
-
console.log("
|
|
196
|
-
console.log(
|
|
197
|
-
console.log("
|
|
198
|
-
console.log("
|
|
199
|
-
console.log(
|
|
200
|
-
console.log(` ${chalk.green("
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
//.command("
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
//
|
|
257
|
-
let
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
.
|
|
386
|
-
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
"let",
|
|
430
|
-
"long",
|
|
431
|
-
"native",
|
|
432
|
-
"new",
|
|
433
|
-
"null",
|
|
434
|
-
"package",
|
|
435
|
-
"private",
|
|
436
|
-
"protected",
|
|
437
|
-
"public",
|
|
438
|
-
"return",
|
|
439
|
-
"short",
|
|
440
|
-
"static",
|
|
441
|
-
"super",
|
|
442
|
-
"switch",
|
|
443
|
-
"synchronized",
|
|
444
|
-
"this",
|
|
445
|
-
"throw",
|
|
446
|
-
"throws",
|
|
447
|
-
"transient",
|
|
448
|
-
"true",
|
|
449
|
-
"try",
|
|
450
|
-
"typeof",
|
|
451
|
-
"var",
|
|
452
|
-
"void",
|
|
453
|
-
"volatile",
|
|
454
|
-
"while",
|
|
455
|
-
"with",
|
|
456
|
-
"yield",
|
|
457
|
-
];
|
|
458
|
-
const builtin = [
|
|
459
|
-
"Array",
|
|
460
|
-
"Date",
|
|
461
|
-
"eval",
|
|
462
|
-
"function",
|
|
463
|
-
"hasOwnProperty",
|
|
464
|
-
"Infinity",
|
|
465
|
-
"isFinite",
|
|
466
|
-
"isNaN",
|
|
467
|
-
"isPrototypeOf",
|
|
468
|
-
"length",
|
|
469
|
-
"Math",
|
|
470
|
-
"name",
|
|
471
|
-
"NaN",
|
|
472
|
-
"Number",
|
|
473
|
-
"Object",
|
|
474
|
-
"prototype",
|
|
475
|
-
"String",
|
|
476
|
-
"toString",
|
|
477
|
-
"undefined",
|
|
478
|
-
"valueOf",
|
|
479
|
-
];
|
|
480
|
-
if (reserved.includes(name) || builtin.includes(name)) {
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
const re = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/;
|
|
484
|
-
if (!re.test(name)) {
|
|
485
|
-
return false;
|
|
486
|
-
}
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import yargs from "yargs";
|
|
3
|
+
import { hideBin } from "yargs/helpers";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import handlebars from "handlebars";
|
|
7
|
+
import { dirname } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
import { readdir } from "fs/promises";
|
|
14
|
+
import { isReservedWord } from "./isReservedWord.js";
|
|
15
|
+
// this is the path of our @m2c2kit/cli program
|
|
16
|
+
// our templates are stored under here
|
|
17
|
+
const packageHomeFolderPath = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
// we store the CLI version in .env. The .env file is created during the
|
|
19
|
+
// build process with the write-dotenv.js script
|
|
20
|
+
// I couldn't get dotenv working with a node cli, so just read it in with
|
|
21
|
+
// readFileSync()
|
|
22
|
+
let cliVersion;
|
|
23
|
+
try {
|
|
24
|
+
cliVersion = fs
|
|
25
|
+
.readFileSync(path.join(packageHomeFolderPath, ".env"))
|
|
26
|
+
.toString()
|
|
27
|
+
.split("=")[1];
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
cliVersion = "UNKNOWN";
|
|
31
|
+
}
|
|
32
|
+
async function getFilenamesRecursive(dir) {
|
|
33
|
+
const dirents = await readdir(dir, { withFileTypes: true });
|
|
34
|
+
const files = await Promise.all(dirents.map((dirent) => {
|
|
35
|
+
const res = resolve(dir, dirent.name);
|
|
36
|
+
return dirent.isDirectory() ? getFilenamesRecursive(res) : res;
|
|
37
|
+
}));
|
|
38
|
+
return Array.prototype.concat(...files);
|
|
39
|
+
}
|
|
40
|
+
async function copyFolderRecursive(args) {
|
|
41
|
+
const sourceFiles = await getFilenamesRecursive(args.sourceFolder);
|
|
42
|
+
const destFiles = sourceFiles.map((f) => path.join(args.destinationFolder, f.replace(args.sourceFolder, "")));
|
|
43
|
+
sourceFiles.forEach((source, index) => {
|
|
44
|
+
const sourceContents = fs.readFileSync(source);
|
|
45
|
+
const destFile = destFiles[index];
|
|
46
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
47
|
+
fs.writeFileSync(destFile, sourceContents);
|
|
48
|
+
console.log(`${chalk.green("COPY")} ${destFile} (${sourceContents.length} bytes)`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
function applyValuesToTemplate(templateFilePath, contents) {
|
|
53
|
+
const templateContents = fs.readFileSync(templateFilePath).toString();
|
|
54
|
+
const template = handlebars.compile(templateContents);
|
|
55
|
+
return template(contents);
|
|
56
|
+
}
|
|
57
|
+
// on the demo server, identify studies with a random 5 digit code
|
|
58
|
+
const generateStudyCode = () => {
|
|
59
|
+
let result = "";
|
|
60
|
+
// omit I, 1, O, and 0 due to potential confusion
|
|
61
|
+
const characters = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
62
|
+
const charactersLength = characters.length;
|
|
63
|
+
for (let i = 0; i < 5; i++) {
|
|
64
|
+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
const yarg = yargs(hideBin(process.argv));
|
|
69
|
+
await yarg
|
|
70
|
+
.scriptName("m2")
|
|
71
|
+
.command("new <name>", "create a new m2c2kit app", (yarg) => {
|
|
72
|
+
yarg.positional("name", {
|
|
73
|
+
describe: "name of new app",
|
|
74
|
+
type: "string",
|
|
75
|
+
});
|
|
76
|
+
}, async (argv) => {
|
|
77
|
+
const appName = argv["name"];
|
|
78
|
+
if (isReservedWord(appName)) {
|
|
79
|
+
console.error(`${chalk.red("ERROR")}: the name ${appName} is not valid. The name must be a valid JavaScript identifer (e.g., begin with a letter, $ or _; not contain spaces; not be a reserved word, etc.).`);
|
|
80
|
+
yarg.exit(1, new Error("invalid name"));
|
|
81
|
+
}
|
|
82
|
+
const className = appName.charAt(0).toUpperCase() + appName.slice(1);
|
|
83
|
+
const newFolderPath = path.join(path.resolve(), appName);
|
|
84
|
+
if (fs.existsSync(newFolderPath)) {
|
|
85
|
+
console.error(`${chalk.red("ERROR")}: folder ${newFolderPath} already exists. Please remove this folder and try again.`);
|
|
86
|
+
yarg.exit(1, new Error("folder exists"));
|
|
87
|
+
}
|
|
88
|
+
fs.mkdirSync(newFolderPath);
|
|
89
|
+
fs.mkdirSync(path.join(newFolderPath, "src"));
|
|
90
|
+
fs.mkdirSync(path.join(newFolderPath, ".vscode"));
|
|
91
|
+
const templateFolderPath = path.join(packageHomeFolderPath, "templates");
|
|
92
|
+
const templates = [
|
|
93
|
+
{
|
|
94
|
+
templateFilePath: path.join(templateFolderPath, "package.json.handlebars"),
|
|
95
|
+
destinationFilePath: path.join(newFolderPath, "package.json"),
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
templateFilePath: path.join(templateFolderPath, "rollup.config.mjs.handlebars"),
|
|
99
|
+
destinationFilePath: path.join(newFolderPath, "rollup.config.mjs"),
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
templateFilePath: path.join(templateFolderPath, "tsconfig.json.handlebars"),
|
|
103
|
+
destinationFilePath: path.join(newFolderPath, "tsconfig.json"),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
templateFilePath: path.join(templateFolderPath, "README.md.handlebars"),
|
|
107
|
+
destinationFilePath: path.join(newFolderPath, "README.md"),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
templateFilePath: path.join(templateFolderPath, "index.ts.handlebars"),
|
|
111
|
+
destinationFilePath: path.join(newFolderPath, "src", "index.ts"),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
templateFilePath: path.join(templateFolderPath, "index.html.handlebars"),
|
|
115
|
+
destinationFilePath: path.join(newFolderPath, "src", "index.html"),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
templateFilePath: path.join(templateFolderPath, "launch.json.handlebars"),
|
|
119
|
+
destinationFilePath: path.join(newFolderPath, ".vscode", "launch.json"),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
templateFilePath: path.join(templateFolderPath, "m2c2kit.json.handlebars"),
|
|
123
|
+
destinationFilePath: path.join(newFolderPath, "m2c2kit.json"),
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
let errorProcessingTemplates = false;
|
|
127
|
+
templates.forEach((t) => {
|
|
128
|
+
try {
|
|
129
|
+
const contents = applyValuesToTemplate(t.templateFilePath, {
|
|
130
|
+
appName: appName,
|
|
131
|
+
className: className,
|
|
132
|
+
cliVersion: cliVersion,
|
|
133
|
+
studyCode: generateStudyCode(),
|
|
134
|
+
});
|
|
135
|
+
fs.writeFileSync(t.destinationFilePath, contents);
|
|
136
|
+
console.log(`${chalk.green("CREATE")} ${t.destinationFilePath} (${contents.length} bytes)`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
errorProcessingTemplates = true;
|
|
140
|
+
console.error(`${chalk.red("ERROR")} ${t.destinationFilePath}`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
if (errorProcessingTemplates) {
|
|
144
|
+
console.error(`${chalk.red("ERROR")}: could not generate files`);
|
|
145
|
+
yarg.exit(1, new Error("template error"));
|
|
146
|
+
}
|
|
147
|
+
const foldersToCopy = [
|
|
148
|
+
{
|
|
149
|
+
sourceFolder: path.join(packageHomeFolderPath, "assets", "cli-starter"),
|
|
150
|
+
destinationFolder: path.join(newFolderPath, "src", "assets", appName),
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
sourceFolder: path.join(packageHomeFolderPath, "assets", "css"),
|
|
154
|
+
destinationFolder: path.join(newFolderPath, "src", "assets", "css"),
|
|
155
|
+
},
|
|
156
|
+
// {
|
|
157
|
+
// sourceFolder: path.join(packageHomeFolderPath, "scripts"),
|
|
158
|
+
// destinationFolder: path.join(newFolderPath),
|
|
159
|
+
// },
|
|
160
|
+
];
|
|
161
|
+
try {
|
|
162
|
+
await Promise.all(foldersToCopy.map(async (c) => copyFolderRecursive(c)));
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error(`${chalk.red("ERROR")}: could not copy files: ${error}`);
|
|
166
|
+
yarg.exit(1, new Error("copy files error"));
|
|
167
|
+
}
|
|
168
|
+
const spinner = ora("Installing packages (npm)...").start();
|
|
169
|
+
let npm;
|
|
170
|
+
if (process.platform === "win32") {
|
|
171
|
+
npm = spawn(`npm`, ["install"], { shell: true, cwd: newFolderPath });
|
|
172
|
+
// npm = spawn(`npm`, ["--version"], { shell: true, cwd: newFolderPath });
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
npm = spawn(`npm`, ["install"], { cwd: newFolderPath });
|
|
176
|
+
// npm = spawn(`npm`, ["--version"], { cwd: newFolderPath });
|
|
177
|
+
}
|
|
178
|
+
let npmOut = "";
|
|
179
|
+
npm.stdout.on("data", (data) => {
|
|
180
|
+
npmOut = npmOut + data.toString();
|
|
181
|
+
});
|
|
182
|
+
npm.stderr.on("data", (data) => {
|
|
183
|
+
npmOut = npmOut + data.toString();
|
|
184
|
+
});
|
|
185
|
+
// node will wait for the npm process to exit. Thus,
|
|
186
|
+
// this on exit listener is the last code we execute before
|
|
187
|
+
// the "m2 new" command is done
|
|
188
|
+
npm.on("exit", (code) => {
|
|
189
|
+
if (code === 0) {
|
|
190
|
+
spinner.succeed("Packages installed successfully");
|
|
191
|
+
console.log(`Created ${appName} at ${newFolderPath}`);
|
|
192
|
+
console.log("Inside that directory, you can run several commands:\n");
|
|
193
|
+
console.log(` ${chalk.green("npm run serve")}`);
|
|
194
|
+
console.log(" Starts the development server.\n");
|
|
195
|
+
console.log(` ${chalk.green("npm run build")}`);
|
|
196
|
+
console.log(" Bundles the app for production.\n");
|
|
197
|
+
// console.log(` ${chalk.green("m2 upload")}`);
|
|
198
|
+
// console.log(" Uploads the app to a test server.\n");
|
|
199
|
+
console.log("We suggest you begin by typing:\n");
|
|
200
|
+
console.log(` ${chalk.green("cd")} ${appName}`);
|
|
201
|
+
console.log(` ${chalk.green("npm run serve")}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
spinner.fail("Error during package installation");
|
|
205
|
+
console.log(npmOut);
|
|
206
|
+
yarg.exit(1, new Error("npm install error"));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
})
|
|
210
|
+
// TODO: move npm script execution into this cli, so there's a single tool
|
|
211
|
+
//.command("build", "compiles to output directory named dist/")
|
|
212
|
+
//.command("serve", "builds and serves app, rebuilding on files changes")
|
|
213
|
+
// .command(
|
|
214
|
+
// "upload",
|
|
215
|
+
// "upload production build from dist/ to server",
|
|
216
|
+
// (yargs) => {
|
|
217
|
+
// yargs
|
|
218
|
+
// .option("url", {
|
|
219
|
+
// alias: "u",
|
|
220
|
+
// type: "string",
|
|
221
|
+
// description: "server url",
|
|
222
|
+
// })
|
|
223
|
+
// .option("studyCode", {
|
|
224
|
+
// alias: "s",
|
|
225
|
+
// type: "string",
|
|
226
|
+
// description:
|
|
227
|
+
// "study code. This 5-digit random alphanumeric code is automatically generated by the cli during the creation of a new app and stored in m2c2kit.json",
|
|
228
|
+
// })
|
|
229
|
+
// .option("remove", {
|
|
230
|
+
// alias: "r",
|
|
231
|
+
// type: "boolean",
|
|
232
|
+
// description: "remove saved server credentials from local machine",
|
|
233
|
+
// });
|
|
234
|
+
// },
|
|
235
|
+
// async (argv) => {
|
|
236
|
+
// const m2c2kitProjectConfig = new Conf({
|
|
237
|
+
// // store this config in the project folder, not user's ~/.config
|
|
238
|
+
// cwd: ".",
|
|
239
|
+
// configName: "m2c2kit",
|
|
240
|
+
// });
|
|
241
|
+
// // store server credentials in user folder, not project folder
|
|
242
|
+
// // to avoid secrets getting into source control
|
|
243
|
+
// const m2c2kitUserConfig = new Conf({
|
|
244
|
+
// configName: "m2c2kit-cli-user",
|
|
245
|
+
// projectName: "m2c2kit-cli",
|
|
246
|
+
// projectSuffix: "",
|
|
247
|
+
// accessPropertiesByDotNotation: false,
|
|
248
|
+
// });
|
|
249
|
+
// if (argv["remove"] as unknown as boolean) {
|
|
250
|
+
// m2c2kitUserConfig.clear();
|
|
251
|
+
// console.log(
|
|
252
|
+
// `all server credentials removed from ${m2c2kitUserConfig.path}`
|
|
253
|
+
// );
|
|
254
|
+
// return;
|
|
255
|
+
// }
|
|
256
|
+
// let abort = false;
|
|
257
|
+
// let serverConfig: serverConfig;
|
|
258
|
+
// try {
|
|
259
|
+
// serverConfig = m2c2kitProjectConfig.get("server") as serverConfig;
|
|
260
|
+
// } catch {
|
|
261
|
+
// serverConfig = {};
|
|
262
|
+
// }
|
|
263
|
+
// // get url first from command line, then from config file, then assign blank
|
|
264
|
+
// let url = (argv["url"] as unknown as string) ?? serverConfig?.url ?? "";
|
|
265
|
+
// if (url === "") {
|
|
266
|
+
// const response = await prompts({
|
|
267
|
+
// type: "text",
|
|
268
|
+
// name: "url",
|
|
269
|
+
// message: "server url?",
|
|
270
|
+
// validate: (text) =>
|
|
271
|
+
// (text as unknown as string).length > 0
|
|
272
|
+
// ? true
|
|
273
|
+
// : "provide a valid server url (e.g., https://www.myserver.com)",
|
|
274
|
+
// onState: (state) => (abort = state.aborted === true),
|
|
275
|
+
// });
|
|
276
|
+
// url = response.url;
|
|
277
|
+
// }
|
|
278
|
+
// if (abort) {
|
|
279
|
+
// yarg.exit(1, new Error("user aborted"));
|
|
280
|
+
// }
|
|
281
|
+
// // get study code first from command line, then from config file, then generate new one
|
|
282
|
+
// const studyCode =
|
|
283
|
+
// (argv["studyCode"] as unknown as string) ??
|
|
284
|
+
// serverConfig?.studyCode ??
|
|
285
|
+
// generateStudyCode();
|
|
286
|
+
// // if new url or study code was provided, save these
|
|
287
|
+
// if (url !== serverConfig?.url || studyCode !== serverConfig?.studyCode) {
|
|
288
|
+
// m2c2kitProjectConfig.set("server", { url, studyCode } as serverConfig);
|
|
289
|
+
// }
|
|
290
|
+
// const serverCredentials = m2c2kitUserConfig.get(
|
|
291
|
+
// url
|
|
292
|
+
// ) as m2c2kitServerCredentials;
|
|
293
|
+
// let username = serverCredentials?.username ?? "";
|
|
294
|
+
// let writeCredentials = false;
|
|
295
|
+
// if (username === "") {
|
|
296
|
+
// const response = await prompts({
|
|
297
|
+
// type: "text",
|
|
298
|
+
// name: "username",
|
|
299
|
+
// message: `username for server at ${url}`,
|
|
300
|
+
// validate: (text) =>
|
|
301
|
+
// (text as unknown as string).length > 0
|
|
302
|
+
// ? true
|
|
303
|
+
// : "username cannot be empty",
|
|
304
|
+
// onState: (state) => (abort = state.aborted === true),
|
|
305
|
+
// });
|
|
306
|
+
// username = response.username;
|
|
307
|
+
// writeCredentials = true;
|
|
308
|
+
// }
|
|
309
|
+
// if (abort) {
|
|
310
|
+
// yarg.exit(1, new Error("user aborted"));
|
|
311
|
+
// }
|
|
312
|
+
// let password = serverCredentials?.password ?? "";
|
|
313
|
+
// if (password === "") {
|
|
314
|
+
// const response = await prompts({
|
|
315
|
+
// type: "password",
|
|
316
|
+
// name: "password",
|
|
317
|
+
// message: `password for server at ${url}`,
|
|
318
|
+
// validate: (text) =>
|
|
319
|
+
// (text as unknown as string).length > 0
|
|
320
|
+
// ? true
|
|
321
|
+
// : "password cannot be empty",
|
|
322
|
+
// onState: (state) => (abort = state.aborted === true),
|
|
323
|
+
// });
|
|
324
|
+
// password = response.password;
|
|
325
|
+
// writeCredentials = true;
|
|
326
|
+
// }
|
|
327
|
+
// if (abort) {
|
|
328
|
+
// yarg.exit(1, new Error("user aborted"));
|
|
329
|
+
// }
|
|
330
|
+
// if (writeCredentials) {
|
|
331
|
+
// m2c2kitUserConfig.set(url, {
|
|
332
|
+
// username,
|
|
333
|
+
// password,
|
|
334
|
+
// } as m2c2kitServerCredentials);
|
|
335
|
+
// console.log(
|
|
336
|
+
// `credentials (username, password) for server ${url} written to ${m2c2kitUserConfig.path}`
|
|
337
|
+
// );
|
|
338
|
+
// }
|
|
339
|
+
// // const getFilenamesRecursive = async (dir: string): Promise<string[]> => {
|
|
340
|
+
// // const dirents = await readdir(dir, { withFileTypes: true });
|
|
341
|
+
// // const files = await Promise.all(
|
|
342
|
+
// // dirents.map((dirent) => {
|
|
343
|
+
// // const res = resolve(dir, dirent.name);
|
|
344
|
+
// // return dirent.isDirectory() ? getFilenamesRecursive(res) : res;
|
|
345
|
+
// // })
|
|
346
|
+
// // );
|
|
347
|
+
// // return Array.prototype.concat(...files);
|
|
348
|
+
// // };
|
|
349
|
+
// const uploadFile = async (
|
|
350
|
+
// serverUrl: string,
|
|
351
|
+
// userPw: string,
|
|
352
|
+
// studyCode: string,
|
|
353
|
+
// fileContent: Buffer,
|
|
354
|
+
// filename: string
|
|
355
|
+
// ) => {
|
|
356
|
+
// const basename = path.basename(filename);
|
|
357
|
+
// let folderName = filename.replace(basename, "");
|
|
358
|
+
// folderName = folderName.replace(/\/$/, ""); // no trailing slash
|
|
359
|
+
// const form = new FormData();
|
|
360
|
+
// form.append("folder", folderName);
|
|
361
|
+
// form.append("file", fileContent, filename);
|
|
362
|
+
// const userPwBase64 = Buffer.from(userPw, "utf-8").toString("base64");
|
|
363
|
+
// const uploadUrl = `${serverUrl}/api/studies/${studyCode}/files`;
|
|
364
|
+
// return axios.post(uploadUrl, form, {
|
|
365
|
+
// headers: {
|
|
366
|
+
// ...form.getHeaders(),
|
|
367
|
+
// // this uses basic auth because it's simply a demo server
|
|
368
|
+
// // but for production, a more robust auth must be used
|
|
369
|
+
// Authorization: `Basic ${userPwBase64}`,
|
|
370
|
+
// },
|
|
371
|
+
// });
|
|
372
|
+
// };
|
|
373
|
+
// const distFolder = path.join(process.cwd(), "dist");
|
|
374
|
+
// if (!fs.existsSync(distFolder)) {
|
|
375
|
+
// console.error(
|
|
376
|
+
// `${chalk.red(
|
|
377
|
+
// "ERROR"
|
|
378
|
+
// )}: distribution folder not found. looked for ${distFolder}`
|
|
379
|
+
// );
|
|
380
|
+
// yarg.exit(1, new Error("no files"));
|
|
381
|
+
// }
|
|
382
|
+
// const files = await getFilenamesRecursive(distFolder);
|
|
383
|
+
// if (files.length === 0) {
|
|
384
|
+
// console.error(
|
|
385
|
+
// `${chalk.red("ERROR")}: no files for upload in ${distFolder}`
|
|
386
|
+
// );
|
|
387
|
+
// yarg.exit(1, new Error("no files"));
|
|
388
|
+
// }
|
|
389
|
+
// const uploadRequests = files.map((file) => {
|
|
390
|
+
// let relativeName = file;
|
|
391
|
+
// if (relativeName.startsWith(distFolder)) {
|
|
392
|
+
// relativeName = relativeName.slice(distFolder.length);
|
|
393
|
+
// // on server, normalize all paths to forward slash
|
|
394
|
+
// relativeName = relativeName.replace(/\\/g, "/");
|
|
395
|
+
// if (relativeName.startsWith("/")) {
|
|
396
|
+
// relativeName = relativeName.slice(1);
|
|
397
|
+
// }
|
|
398
|
+
// }
|
|
399
|
+
// const fileBuffer = fs.readFileSync(file);
|
|
400
|
+
// return uploadFile(
|
|
401
|
+
// url,
|
|
402
|
+
// `${username}:${password}`,
|
|
403
|
+
// studyCode,
|
|
404
|
+
// fileBuffer,
|
|
405
|
+
// relativeName
|
|
406
|
+
// );
|
|
407
|
+
// });
|
|
408
|
+
// const spinner = ora(`Uploading files to ${url}...`).start();
|
|
409
|
+
// Promise.all(uploadRequests)
|
|
410
|
+
// .then(() => {
|
|
411
|
+
// spinner.succeed(`All files uploaded from ${distFolder}`);
|
|
412
|
+
// console.log(
|
|
413
|
+
// `Study can be viewed with browser at ${url}/studies/${studyCode}`
|
|
414
|
+
// );
|
|
415
|
+
// })
|
|
416
|
+
// .catch((error) => {
|
|
417
|
+
// spinner.fail(`Failed uploading files from ${distFolder}`);
|
|
418
|
+
// console.error(`${chalk.red("ERROR")}: ${error}`);
|
|
419
|
+
// yarg.exit(1, new Error("upload error"));
|
|
420
|
+
// });
|
|
421
|
+
// }
|
|
422
|
+
// )
|
|
423
|
+
.demandCommand()
|
|
424
|
+
.strict()
|
|
425
|
+
// seems to have problems getting version automatically; thus I explicity
|
|
426
|
+
// provide
|
|
427
|
+
.version(cliVersion)
|
|
428
|
+
.showHelpOnFail(true).argv;
|