@caweb/cli 1.11.0 → 1.11.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.
@@ -0,0 +1,614 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { select } from 'inquirer-select-pro';
5
+ import chalk from 'chalk';
6
+ import { writeLine, clearLine } from '../../lib/index.js';
7
+
8
+ // since we are in a spinner,
9
+ // we have to silence all the cancellation errors when using prompts
10
+ // .catch(() => {process.exit(1);})
11
+ import { confirm, input, password } from '@inquirer/prompts';
12
+
13
+
14
+ const bullet = chalk.yellow('-');
15
+ const info = chalk.cyan('i');
16
+
17
+ /**
18
+ * Default Header Navigation for the site data.
19
+ */
20
+ const headerNav = [
21
+ {
22
+ "label": "CAWebPublishing",
23
+ "url": "https://caweb.cdt.ca.gov/",
24
+ },
25
+ {
26
+ "label": "CDT",
27
+ "url": "https://cdt.ca.gov/",
28
+ "sub": [
29
+ {
30
+ "label": "Accessibility",
31
+ "url": "https://caweb.cdt.ca.gov/accessibility-2/",
32
+ "description": "Accessibility"
33
+ },
34
+ ]
35
+ }
36
+ ];
37
+
38
+ /**
39
+ * Default Utility Links for the site data.
40
+ */
41
+ const utilityLinks = [];
42
+
43
+ /**
44
+ * Default Footer Navigation for the site data.
45
+ */
46
+ const footerNav = [
47
+ {
48
+ "label": "Accessibility",
49
+ "url": "https://caweb.cdt.ca.gov/accessibility-2/",
50
+ },
51
+ ];
52
+
53
+ /**
54
+ * Default Social Media Platforms for the site data.
55
+ */
56
+ const socialPlatforms = {
57
+ email : 'Share via Email',
58
+ facebook: 'Facebook',
59
+ github: 'Github',
60
+ instagram: 'Instagram',
61
+ linkedin: 'LinkedIn',
62
+ twitter: 'X',
63
+ youtube: 'YouTube',
64
+ }
65
+
66
+ /**
67
+ * Default Link Options for the prompts.
68
+ */
69
+ const linkOptions = {
70
+ label: {default: ''},
71
+ target: {default: '_blank'},
72
+ desc: {default: ''}
73
+ }
74
+
75
+ /**
76
+ * Prompt for header configurations
77
+ *
78
+ * - Utility Header
79
+ * - Navigation
80
+ *
81
+ * @returns {object}
82
+ */
83
+ async function promptForHeader(){
84
+ writeLine('Header Configurations', {color: 'magenta', char: '#', borderColor: 'magenta'});
85
+ writeLine('The following features are available for the header.', {color: 'cyan', prefix: 'i'});
86
+ writeLine('Anything not configured will be set to default settings.', {color: 'cyan', prefix: 'i'});
87
+
88
+ let defaultFeatures = [{'utility-header': {checked: false}}, {'navigation': {checked: true}}];
89
+
90
+ // default header object
91
+ let header = {
92
+ utility: utilityLinks,
93
+ nav: headerNav,
94
+ };
95
+
96
+ let options = [];
97
+
98
+ // if no features are passed, set the default features
99
+ defaultFeatures.forEach(feature => {
100
+ let keyName = Object.keys(feature)[0];
101
+ let checked = feature[keyName].checked;
102
+
103
+ options.push({
104
+ // replace - with space and make first letter upper case
105
+ name: keyName.replace('-', ' ').replace(/^\w| \w/g, c => c.toUpperCase()),
106
+ value: keyName,
107
+ checked
108
+ })
109
+ })
110
+
111
+ // prompt for the features to configure
112
+ let features = await select({
113
+ message: 'Select which of the following features you would like to configure...\n',
114
+ multiple: true,
115
+ canToggleAll: true,
116
+ options,
117
+ defaultValue: options.map(o => o.checked ? o.value : false ).filter( e => e)
118
+ }, {clearPromptOnDone: true}).catch(() => {process.exit(1);});
119
+
120
+ // if utility-header is selected, prompt for utility header configurations.
121
+ if( features.includes('utility-header') ){
122
+
123
+ writeLine('Utility Header', {bold: true});
124
+ writeLine('The Utility Header are 3 custom links that are displayed at the very top of the page.', {color: 'cyan', prefix: 'i'});
125
+
126
+ let linkCount = 1;
127
+ let doneUtilityLinks = false;
128
+
129
+ // set the header utility to an empty array
130
+ header.utility = [];
131
+
132
+ do{
133
+
134
+ writeLine(`Utility Link ${linkCount}`, {bold: true});
135
+
136
+ header.utility.push( await promptForLink() );
137
+
138
+ doneUtilityLinks = await confirm(
139
+ {
140
+ message: 'Would you like to add another link?',
141
+ default: false
142
+ },
143
+ { clearPromptOnDone: true }
144
+ ).catch(() => {process.exit(1);});
145
+
146
+ // if the user does not want to add another link, break the loop
147
+ if( ! doneUtilityLinks ){
148
+ break;
149
+ }
150
+ // if the user wants to add another link, increment the link count
151
+ linkCount++;
152
+ } while( linkCount < 4 );
153
+
154
+ // clear lines for each link plus the link stored message
155
+ // + 2 for the header and info message
156
+ clearLine((linkCount * 3) + 2);
157
+ }
158
+
159
+ // if navigation is selected, prompt for navigation configurations.
160
+ if( features.includes('navigation') ){
161
+
162
+ writeLine('Navigation', {bold: true});
163
+ writeLine('The Navigation is a set of links that are displayed to navigate your site.', {color: 'cyan', prefix: 'i'});
164
+
165
+ // ask if the user wants to use sample links
166
+ let sampleNavLinks = await confirm(
167
+ {
168
+ message: 'Would you like to use the sample links?',
169
+ default: true
170
+ },
171
+ { clearPromptOnDone: true }
172
+ ).catch(() => {process.exit(1);});
173
+
174
+ // if the user does not want to use sample links, prompt for the links
175
+ if( ! sampleNavLinks ){
176
+
177
+ // set the header nav to a nav array.
178
+ header.nav = await promptForNavigation();
179
+
180
+ }
181
+
182
+ // clear lines for each link plus the link stored message
183
+ // + 2 for the header and info message
184
+ clearLine((header.nav.length * 3) + 2);
185
+ }
186
+
187
+ // clear lines
188
+ clearLine(6);
189
+ writeLine('Header Configurations Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
190
+
191
+ // return the header object
192
+ return header
193
+ }
194
+
195
+ /**
196
+ * Prompt for footer configurations
197
+ *
198
+ * @returns {object}
199
+ */
200
+ async function promptForFooter(){
201
+ // write the header line
202
+ writeLine('Footer Configurations', {color: 'magenta', char: '#', borderColor: 'magenta'});
203
+
204
+ // default footer object
205
+ let footer = {
206
+ nav: footerNav,
207
+ };
208
+
209
+
210
+ // write the footer line
211
+ writeLine('Navigation', {bold: true});
212
+ writeLine('The Navigation is a set of links that are displayed at the bottom of your site.', {color: 'cyan', prefix: 'i'});
213
+
214
+ // ask if the user wants to use sample links
215
+ let sampleNavLinks = await confirm(
216
+ {
217
+ message: 'Would you like to use the sample links?',
218
+ default: true
219
+ },
220
+ { clearPromptOnDone: true }
221
+ ).catch(() => {process.exit(1);});
222
+
223
+ // if the user does not want to use sample links, prompt for the links
224
+ if( ! sampleNavLinks ){
225
+
226
+ // set the footer nav to a nav array.
227
+ footer.nav = await promptForNavigation(false);
228
+
229
+ }
230
+
231
+ // clear lines for each link plus the link stored message
232
+ // + 5 for the header and info message
233
+ clearLine((footer.nav.length * 3) + 5);
234
+
235
+ writeLine('Footer Configurations Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
236
+
237
+ // return the header object
238
+ return footer
239
+ }
240
+
241
+ /**
242
+ * Prompt for social media links
243
+ *
244
+ * @returns {object}
245
+ */
246
+ async function promptForSocial(){
247
+ let socials = {};
248
+ // write the header line
249
+ writeLine('Social Media Links', {color: 'magenta', char: '#', borderColor: 'magenta'});
250
+ writeLine('The following social media platforms are available.', {color: 'cyan', prefix: 'i'});
251
+
252
+ let options = [];
253
+
254
+ Object.keys(socialPlatforms).forEach(s => {
255
+ options.push({
256
+ name: socialPlatforms[s],
257
+ value: s,
258
+ checked: false,
259
+ })
260
+ })
261
+
262
+ let platforms = await select({
263
+ message: 'Select which of the following social media platforms you would like to configure...\n',
264
+ multiple: true,
265
+ canToggleAll: true,
266
+ options,
267
+ defaultValue: options.map(o => o.checked ? o.value : false ).filter( e => e)
268
+ }, {clearPromptOnDone: true}).catch(() => {process.exit(1);});
269
+
270
+
271
+ for( const platform of platforms ){
272
+ // prompt for the social media links
273
+ writeLine(socialPlatforms[platform], {bold: true});
274
+ socials[platform] = await promptForLink({});
275
+ }
276
+
277
+ // clear lines for each link plus the link stored message
278
+ // + 4 for the header and info message
279
+ clearLine((Object.keys(socials).length * 3) + 4);
280
+
281
+ writeLine('Social Media Links Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
282
+
283
+ return socials;
284
+ }
285
+
286
+ /**
287
+ * Prompt for site wide alerts
288
+ *
289
+ * @returns {object}
290
+ */
291
+ async function promptForAlerts(){
292
+ // write the header line
293
+ writeLine('Site Wide Alerts', {color: 'magenta', char: '#', borderColor: 'magenta'});
294
+ writeLine('The site wide alerts appear at the top of the page.', {color: 'cyan', prefix: 'i'});
295
+
296
+ let alerts = [];
297
+
298
+ // ask if the user wants to add an alert.
299
+ if((await confirm(
300
+ {
301
+ message: 'Would you like to add an alert?',
302
+ default: false
303
+ },
304
+ { clearPromptOnDone: true }
305
+ ).catch(() => {process.exit(1);}))
306
+ ){
307
+ let doneAlerts = false
308
+
309
+ do{
310
+ let alert = {
311
+ icon: await input({ message: 'What is the alert icon?', default: 'info'}).catch(() => {process.exit(1);}),
312
+ header: await input({ message: 'What is the alert header?'}).catch(() => {process.exit(1);}),
313
+ msg: await input({ message: 'What is the alert message?'}).catch(() => {process.exit(1);}),
314
+ }
315
+
316
+
317
+ // ask if the user wants to add a readmore link
318
+ if(
319
+ await confirm(
320
+ {
321
+ message: 'Would you like to add a read more button?',
322
+ default: true
323
+ },
324
+ { clearPromptOnDone: true }
325
+ ).catch(() => {process.exit(1);})
326
+ ){
327
+ writeLine('Read More Link', {bold: true});
328
+ alert.link = await promptForLink({label:{default: 'Read More'}, target: {default: '_self'}});
329
+ }
330
+
331
+ // push the alert to the alerts array
332
+ alerts.push(alert);
333
+
334
+ // ask if the user wants to add another alert
335
+ doneAlerts = await confirm(
336
+ {
337
+ message: 'Would you like to add another alert?',
338
+ default: false
339
+ },
340
+ { clearPromptOnDone: true }
341
+ ).catch(() => {process.exit(1);});
342
+
343
+ // clear lines for each alert field
344
+ // if there's a a link remove only 2 lines, since 1 line is removed due to the link key in the alert object
345
+ clearLine(Object.keys(alert).length + (alert.link ? 2 : 0));
346
+
347
+ writeLine('Alert Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
348
+ }while( doneAlerts );
349
+
350
+ // clear lines for each alert plus the alert stored messages
351
+ // + 4 for the header and info message
352
+ clearLine((alerts.length * 3) + 4);
353
+
354
+ }
355
+
356
+ // clear the header and info lines
357
+ clearLine(4);
358
+
359
+ writeLine('Alerts Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
360
+
361
+ return alerts;
362
+ }
363
+
364
+ /**
365
+ * Prompt for generic links
366
+ *
367
+ * @param {object} opts Additional prompts for the links. Choices are: ['all', 'label', 'target', 'desc']
368
+ * @returns { label, url, target }
369
+ */
370
+ async function promptForLink(opts = {label: {}, target: {}}){
371
+ // URL is always prompted for
372
+ // all other options are only prompted if they are in the opts array
373
+ let link = {
374
+ url: await input({
375
+ message: 'Link URL:',
376
+ required: true,
377
+ validate: (input) => {
378
+ if( ! input.startsWith('http') && ! input.startsWith('https') ){
379
+ return 'Link URL must start with http or https.';
380
+ }
381
+ return true;
382
+ }
383
+ }).catch(() => {process.exit(1);}),
384
+ }
385
+
386
+ let optKeys = Object.keys(opts);
387
+
388
+ // if the user want to prompt for all options
389
+ if( optKeys.includes('all') ){
390
+ opts = {
391
+ ...linkOptions,
392
+ ...optKeys.all
393
+ }
394
+ }
395
+
396
+ // if the user wants to add a label, prompt for it
397
+ if( optKeys.includes('label') || optKeys.includes('all') ){
398
+ link.label = await input(
399
+ {
400
+ message: 'Link Label:',
401
+ required: true,
402
+ default: opts.label.default ? opts.label.default : ''
403
+ }
404
+ ).catch(() => {process.exit(1);})
405
+ }
406
+
407
+ // if the user wants to add a target, prompt for it
408
+ if( optKeys.includes('target') || optKeys.includes('all') ){
409
+ link.target = await select({
410
+ message: 'Link Target:',
411
+ multiple: false,
412
+ options: [
413
+ { name: '_blank', value: '_blank', checked: ! opts.target.default || opts.target.default === '_blank' },
414
+ { name: '_self', value: '_self', checked: opts.target.default === '_self' },
415
+ ]
416
+ })
417
+ .catch(() => {process.exit(1);})
418
+ }
419
+
420
+ // if the user wants to add a description, prompt for it
421
+ if( optKeys.includes('desc') || optKeys.includes('all') ){
422
+ link.description = await input({ message: 'Link Description:' }).catch(() => {process.exit(1);})
423
+ }
424
+
425
+ // clear lines for each prompt plus the link main message
426
+ clearLine(Object.keys(link).length + 1);
427
+
428
+ writeLine('Link Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
429
+ return link;
430
+ }
431
+
432
+ /**
433
+ * Prompt for navigation
434
+ *
435
+ */
436
+ async function promptForNavigation(sublinks = true){
437
+
438
+ let linkCount = 1;
439
+ let doneNavLinks = false;
440
+ let navigation = [];
441
+
442
+ do{
443
+ // main link count message
444
+ writeLine(`Main Link ${linkCount}`, { bold: true});
445
+
446
+ // prompt for the main link
447
+ let mainLink = await promptForLink();
448
+
449
+ // ask if the user wants to add sub links
450
+ if( sublinks && await confirm(
451
+ { message: 'Would you like to add sub links?', default: false },
452
+ { clearPromptOnDone: true }
453
+ ).catch(() => {process.exit(1);})
454
+ ){
455
+
456
+ // set the sub links to an empty array
457
+ mainLink.subLinks = [];
458
+
459
+ let doneSublinks = false;
460
+ let subLinkCount = 0;
461
+
462
+ do{
463
+ writeLine(`Sub Link ${subLinkCount + 1}`, {bold: true});
464
+
465
+ // prompt for the sub link
466
+ mainLink.subLinks.push( await promptForLink({all:{}}) );
467
+
468
+ // ask if the user wants to add another sub link
469
+ doneSublinks = await confirm(
470
+ {
471
+ message: 'Would you like to add another sub link?',
472
+ default: false
473
+ },
474
+ { clearPromptOnDone: true }
475
+ ).catch(() => {process.exit(1);});
476
+
477
+ // increment the sub link count
478
+ subLinkCount++;
479
+ } while( doneSublinks );
480
+
481
+
482
+ // clear lines for each sub link plus the link stored message
483
+ clearLine(subLinkCount * 3);
484
+ }
485
+
486
+ // push the main link to the navigation array
487
+ navigation.push( mainLink );
488
+
489
+ doneNavLinks = await confirm(
490
+ {
491
+ message: 'Would you like to add another main link?',
492
+ default: true
493
+ },
494
+ { clearPromptOnDone: true }
495
+ ).catch(() => {process.exit(1);});
496
+
497
+ // clear lines for each main link plus the link stored message
498
+ // clearLine(4);
499
+
500
+ // increment the link count
501
+ linkCount++;
502
+ } while( doneNavLinks );
503
+
504
+ // return the navigation array
505
+ return navigation;
506
+
507
+ }
508
+
509
+ /**
510
+ * Prompt for General Site Information
511
+ *
512
+ * - Site Title
513
+ *
514
+ * @param {string} title Site title default.
515
+ * @returns {object}
516
+ */
517
+ async function promptForGeneralInfo(title){
518
+ writeLine('General Site Information', {color: 'magenta', char: '#', borderColor: 'magenta'});
519
+
520
+ let info = {
521
+ title: await input({
522
+ message: 'What is the title of the site?',
523
+ default: title,
524
+ required: true,
525
+ },
526
+ {
527
+ clearPromptOnDone: true,
528
+ }
529
+ ).catch(() => {process.exit(1);})
530
+ };
531
+
532
+ // clear lines.
533
+ clearLine(3);
534
+
535
+ writeLine('General Site Information Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
536
+ return info;
537
+ }
538
+
539
+ /**
540
+ * Prompt for Google Settings
541
+ *
542
+ */
543
+ async function promptForGoogleOptions(){
544
+ writeLine('Google Options', {color: 'magenta', char: '#', borderColor: 'magenta'});
545
+ writeLine('The following features are available services provided by Google.', {color: 'cyan', prefix: 'i'});
546
+
547
+ let defaultFeatures = [
548
+ {'search': {checked: true}},
549
+ /**
550
+ * @todo: create prompts for the following features
551
+ */
552
+ // {'analytics': {checked: false}},
553
+ // {'analytics-4': {checked: false}},
554
+ // {'tag-manager': {checked: false}},
555
+ // {'meta': {checked: false}},
556
+ // {'translate': {checked: false}}
557
+ ];
558
+ let google = {};
559
+ let options = [];
560
+
561
+ // if no features are passed, set the default features
562
+ defaultFeatures.forEach(feature => {
563
+ let keyName = Object.keys(feature)[0];
564
+ let checked = feature[keyName].checked;
565
+
566
+ options.push({
567
+ // replace - with space and make first letter upper case
568
+ name: keyName.replace('-', ' ').replace(/^\w| \w/g, c => c.toUpperCase()),
569
+ value: keyName,
570
+ checked
571
+ })
572
+ })
573
+
574
+ // prompt for the features to configure
575
+ let features = await select({
576
+ message: 'Select which of the following features you would like to configure...\n',
577
+ multiple: true,
578
+ canToggleAll: true,
579
+ options,
580
+ defaultValue: options.map(o => o.checked ? o.value : false ).filter( e => e)
581
+ }, {clearPromptOnDone: true}).catch(() => {process.exit(1);});
582
+
583
+ // if search is selected, prompt for search configurations.
584
+ if( features.includes('search') ){
585
+
586
+ writeLine('Search Engine ID', {bold: true});
587
+ writeLine('Enter your unique Google search engine ID, if you don\'t have one see an administrator.', {color: 'cyan', prefix: 'i'});
588
+
589
+ google.search = await input({ message: 'ID:'},{clearPromptOnDone: true}).catch(() => {process.exit(1);})
590
+
591
+ // clear lines
592
+ clearLine(2);
593
+
594
+ writeLine('Google Search Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
595
+ }
596
+
597
+ // clear lines for each google feature
598
+ // + 4 for the header and info message
599
+ clearLine((Object.keys(google).length * 3) + 4);
600
+
601
+ writeLine('Google Options Stored.', {color: 'yellow', char: '#', borderColor: 'yellow'});
602
+ return google;
603
+ }
604
+
605
+ export {
606
+ bullet,
607
+ info,
608
+ promptForGeneralInfo,
609
+ promptForSocial,
610
+ promptForGoogleOptions,
611
+ promptForAlerts,
612
+ promptForHeader,
613
+ promptForFooter
614
+ }
@@ -8,8 +8,6 @@ import fs from 'fs';
8
8
  * Internal dependencies
9
9
  */
10
10
  import {
11
- appPath,
12
- projectPath,
13
11
  runCmd
14
12
  } from '../../lib/index.js';
15
13
 
@@ -31,8 +29,8 @@ export default async function webpack({
31
29
  } ) {
32
30
  const webpackCommand = 'build' === process.argv[2] ? 'build' : 'serve' ;
33
31
 
34
- // we use our default config from the @caweb/html-webpack-plugin
35
- const defaultConfigPath = path.resolve('node_modules', '@caweb', 'html-webpack-plugin', 'webpack.config.js' );
32
+ // we use our default config from the @caweb/webpack
33
+ const defaultConfigPath = path.resolve('node_modules', '@caweb', 'webpack', 'webpack.config.js' );
36
34
 
37
35
  // Since we use @wordpress/scripts webpack config we can leverage
38
36
  // the environment variables as well.