@caweb/cli 1.11.1 → 1.12.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.
@@ -3,35 +3,32 @@
3
3
  */
4
4
  import path from 'path';
5
5
  import fs from 'fs';
6
- import { confirm } from '@inquirer/prompts';
7
6
  import chalk from 'chalk';
8
- import { HTMLToJSON } from 'html-to-json-parser';
9
7
  import jsdom from 'jsdom';
10
8
  import { parse } from 'node-html-parser';
11
9
 
12
10
  /**
13
11
  * Internal dependencies
14
12
  */
15
- import { appPath, writeLine } from '../../lib/index.js';
13
+ import { appPath, projectPath, writeLine } from '../../lib/index.js';
16
14
 
17
- import {
18
- promptForGeneralInfo,
19
- promptForSocial,
20
- promptForGoogleOptions,
21
- promptForAlerts,
22
- promptForHeader,
23
- promptForFooter
24
- } from './prompts.js';
15
+ import validatePage from './validation.js';
25
16
 
26
17
  /**
27
18
  * Constants used during the conversion process.
28
19
  */
20
+ const { DIVI_VER } = JSON.parse( fs.readFileSync( path.join(projectPath, 'package.json') ) ).config;
21
+
29
22
  /**
30
23
  * Modules allow list
31
24
  */
25
+ const commonElements = ['div', 'a', 'b', 'p', 'br', 'span', 'strong', 'ul', 'ol', 'li'];
26
+
27
+
32
28
  const allowedModules = {
33
- 'et_pb_text': ['p', 'span', 'a', 'b'],
34
- 'et_pb_heading': ['span', 'a', 'b']
29
+ 'et_pb_text': [...commonElements, 'code'],
30
+ 'et_pb_heading': ['span', 'a', 'b'],
31
+ 'et_pb_code': commonElements,
35
32
  }
36
33
 
37
34
 
@@ -57,37 +54,6 @@ function getLimiter( type ) {
57
54
  return limiter;
58
55
  }
59
56
 
60
- function findElementByClass(node, search ) {
61
- let found = false;
62
-
63
- node.forEach( n => {
64
- let classes = n.classList ? [...n.classList.values()] : [];
65
-
66
- if( classes.includes(search) ) {
67
- // we found the element we are looking for
68
- // we want to return the element
69
- found = n.childNodes[0].toString();
70
- }else{
71
- found = findElementByClass(n.childNodes, search);
72
- }
73
- });
74
-
75
- return found;
76
- }
77
-
78
- function findSiblingElement(node, search) {
79
- let found = false;
80
- node.forEach( n => {
81
- if( search === n.rawTagName ) {
82
- // we found the element we are looking for
83
- // we want to return the element
84
- found = n;
85
- }
86
- });
87
-
88
- return found;
89
- }
90
-
91
57
  /**
92
58
  * This function is used to sanitize the JSON data.
93
59
  *
@@ -138,7 +104,7 @@ function convertRawAttrs( rawAttrs ) {
138
104
 
139
105
  let attrs = {};
140
106
 
141
- // attributes follow the format of key="value" key="value"
107
+ // attributes follow the format of key="value"
142
108
  // we want to split the string by "space
143
109
  let attrArray = rawAttrs.split( '" ' );
144
110
 
@@ -182,21 +148,65 @@ function getAttrString(attributes, limiter) {
182
148
  case 'style':
183
149
  key = [];
184
150
 
185
- value.split(';').forEach( (v) => {
151
+ value.split(';').filter(Boolean).forEach( (v) => {
152
+
186
153
  let k = v.split(':')[0].trim();
187
154
  let s = v.split(':')[1].trim();
188
155
 
189
156
  // style mappings
190
157
  switch( k ) {
158
+ case 'background-color':
159
+ k = 'background_color';
160
+ break;
191
161
  case 'background-image':
192
162
  k = 'background_image';
193
- s = s.replace(/url\((.*?)\).*/g, '$1').replace(/["']/g, '');
163
+
164
+ let gradient = s.replace(/.*[,\s]*linear-gradient\((.*?)\).*/g, '$1').replace(/["']/g, '');
165
+
166
+ if( gradient ) {
167
+ // if the gradient is present we want to add the appropriate values
168
+ let gradientValues = gradient.split(',');
169
+
170
+ key.push(`background_color_gradient_direction="${gradientValues[0].trim()}"`);
171
+ key.push(`background_color_gradient_stops="${gradientValues.slice(1).join('|')}"`);
172
+ key.push('use_background_color_gradient="on"');
173
+ }
174
+
175
+ s = s.replace(/url\((.*?)\).*/g, '$1').replace(/["']/g, '');
194
176
  break;
195
177
 
196
178
  case 'background-repeat':
197
- k = 'background_position';
179
+ k = 'background_repeat';
198
180
  s = s.replace(/(.*?)(repeat|no-repeat)/g, '$2');
199
181
  break;
182
+
183
+ case 'background-position':
184
+ k = 'background_position';
185
+ /**
186
+ * position can be 4 different syntaxes so lets split the values
187
+ * @link https://developer.mozilla.org/en-US/docs/Web/CSS/background-position
188
+ */
189
+ let values = s.split(' ');
190
+ // for whatever reason Divi has these values inverted
191
+
192
+ switch( values.length ) {
193
+ case 1:
194
+ s = values[0];
195
+ break;
196
+ case 2:
197
+ s = `${values[1]}_${values[0]}`;
198
+ break;
199
+ case 3:
200
+ case 4:
201
+ s = `${values[2]}_${values[0]}`;
202
+ break;
203
+ }
204
+ break;
205
+
206
+ case 'background-size':
207
+ k = 'background_size';
208
+
209
+ break;
200
210
  default:
201
211
  k = '';
202
212
  break;
@@ -210,8 +220,11 @@ function getAttrString(attributes, limiter) {
210
220
 
211
221
  }
212
222
  }
213
-
223
+
214
224
  if( '' !== key && 'string' === typeof key ) {
225
+ // we have to encode certain characters
226
+ value = value.replaceAll('"', '%22');
227
+
215
228
  attrString += ` ${key}="${value}"`;
216
229
  }else if( 'object' === typeof key ) {
217
230
  attrString += ` ${key.join(' ')}`;
@@ -258,7 +271,9 @@ function addElement( tag, opts = {} ) {
258
271
  return content;
259
272
  }
260
273
 
261
- let output = `${lmtrObj.open}${tag}${attrString}${tag && isSelfClosing ? ' /' : ''}${lmtrObj.close}${content.trim()}`
274
+ content = content.trim();
275
+
276
+ let output = `${lmtrObj.open}${tag}${attrString}${tag && isSelfClosing ? ' /' : ''}${lmtrObj.close}${content}`
262
277
 
263
278
  if( ! isUnclosed && ! isSelfClosing && tag) {
264
279
  output += closeElement(tag, lmtrObj );
@@ -324,13 +339,13 @@ function generateShortcodes( mainContent, opts = {
324
339
 
325
340
  // we convert the raw attributes to a json object
326
341
  let attrs = convertRawAttrs( rawAttrs );
327
-
342
+
328
343
  /**
329
344
  * We dont want to convert empty elements
330
345
  */
331
346
  if( ! content.length ){
332
- // image tags are self closing and don't have content this is ok.
333
- if( 'img' === type ) {
347
+ // image tags and br are self closing and don't have content this is ok.
348
+ if( ['img', 'br'].includes( type ) ) {
334
349
  // do nothing this is ok.
335
350
  // no length no type blank spaces
336
351
  } else if( '' !== type ){
@@ -360,6 +375,10 @@ function generateShortcodes( mainContent, opts = {
360
375
  if( classes.includes('container-fluid') && ! opts.inFullwidth) {
361
376
  opts.inFullwidth = true;
362
377
 
378
+ // fullwidth sections don't need the container-fluid class
379
+ // we remove the container-fluid class from the attributes
380
+ attrs.class = attrs.class.replace('container-fluid', '');
381
+
363
382
  let hasContainers = content.filter((r,i) => {
364
383
  if(r.attributes.class.match(/container/g)){
365
384
  // we add the raw attributes to the containers
@@ -372,10 +391,17 @@ function generateShortcodes( mainContent, opts = {
372
391
 
373
392
  if( ! hasContainers ) {
374
393
  output += addElement(
375
- 'et_pb_fullwidth_section',
394
+ 'et_pb_section',
376
395
  {
377
396
  limiter:opts.limiter,
378
- content: generateShortcodes(content, opts)
397
+ content: generateShortcodes(content, opts),
398
+ attrs: {
399
+ _builder_version: DIVI_VER,
400
+ fullwidth: 'on',
401
+ inner_width: 'auto',
402
+ inner_max_width: '1080px',
403
+ ...attrs,
404
+ }
379
405
  }
380
406
  );
381
407
  }else{
@@ -406,7 +432,10 @@ function generateShortcodes( mainContent, opts = {
406
432
  {
407
433
  limiter:opts.limiter,
408
434
  content: generateShortcodes(content, opts),
409
- attrs
435
+ attrs: {
436
+ _builder_version: DIVI_VER,
437
+ ...attrs,
438
+ }
410
439
  }
411
440
  );
412
441
 
@@ -461,17 +490,29 @@ function generateShortcodes( mainContent, opts = {
461
490
  let colType = '';
462
491
  // calculate the column type
463
492
  switch( colSize ) {
464
- // 1 column
465
- case 12:
466
- colType = '4_4';
493
+ // 1/4 columns
494
+ case 3:
495
+ colType = '1_4';
467
496
  break;
468
- // 2 columns
497
+ // 1/3 columns
498
+ case 4:
499
+ colType = '1_3';
500
+ break;
501
+ // 1/2 columns
469
502
  case 6:
470
503
  colType = '1_2';
471
504
  break;
472
- // 3 columns
473
- case 4:
474
- colType = '1_3';
505
+ // 2/3 columns
506
+ case 8:
507
+ colType = '2_3';
508
+ break;
509
+ // 3/4 column
510
+ case 9:
511
+ colType = '3_4';
512
+ break;
513
+ // 1 column
514
+ case 12:
515
+ colType = '4_4';
475
516
  break;
476
517
  }
477
518
 
@@ -513,17 +554,23 @@ function generateShortcodes( mainContent, opts = {
513
554
  if( opts.inFullwidth ) {
514
555
  // output += addElement('', { limiter: 'none', content: generateShortcodes(content) });
515
556
  }else{
516
- if( classes.includes('card') && classes.includes('blurb') ){
517
- // this is a blurb module
518
- output += generateModuleShortcode('blurb', content);
557
+ if( classes.includes('card') ){
558
+ // this is Divi blurb module
559
+ if( classes.includes('blurb') ) {
560
+ output += generateModuleShortcode('blurb', htmlElement );
561
+ }else{
562
+ // this is our card module
563
+ output += generateModuleShortcode('ca_card', htmlElement );
564
+ }
519
565
 
520
566
  }else{
521
567
  // figure out what kind of div element we are dealing with
522
568
  output += addElement(
523
569
  type,
524
570
  {
525
- limiter: opts.limiter,
526
- content: generateShortcodes(content, opts)
571
+ limiter: 'angle',
572
+ content: generateShortcodes(content, opts),
573
+ attrs
527
574
  }
528
575
  );
529
576
 
@@ -535,7 +582,17 @@ function generateShortcodes( mainContent, opts = {
535
582
 
536
583
  // code module
537
584
  case 'code':
538
- output += addElement(setFullwidthTag('et_pb_code', opts.inFullwidth), {attrs, limiter: 'bracket', content: generateShortcodes(content)} );
585
+ // if already opened
586
+ let codeElement = 'et_pb_column' === opts.openElement ?
587
+ setFullwidthTag('et_pb_code', opts.inFullwidth) :
588
+ 'code'
589
+
590
+ output += addElement(
591
+ codeElement,
592
+ {
593
+ attrs, limiter: 'code' === codeElement ? 'angle' : 'bracket',
594
+ content: generateShortcodes(content, opts)
595
+ } );
539
596
  break;
540
597
  // header modules
541
598
  case 'h1':
@@ -546,17 +603,10 @@ function generateShortcodes( mainContent, opts = {
546
603
  case 'h6':
547
604
  // we let the h1-h6 process know element is opened
548
605
  // this allows other elements to be added to the header modules
549
- opts.openElement = 'et_pb_heading';
606
+ // opts.openElement = 'et_pb_heading';
550
607
 
551
- output += addElement(
552
- opts.openElement,
553
- {
554
- limiter: opts.limiter,
555
- content: generateShortcodes(content, opts)
556
- }
557
- );
558
-
559
- opts.openElement = false;
608
+ output += generateModuleShortcode('heading', htmlElement);
609
+ // opts.openElement = false;
560
610
  break;
561
611
 
562
612
  case 'img':
@@ -570,11 +620,16 @@ function generateShortcodes( mainContent, opts = {
570
620
  );
571
621
 
572
622
  break;
573
- // all theses elements can go in a text module
623
+ // all theses elements can go in other modules
624
+ // they also need to be added to the allowedModules list
574
625
  case 'a':
575
626
  case 'b':
576
627
  case 'p':
628
+ case 'ol':
629
+ case 'ul':
630
+ case 'li':
577
631
  case 'span':
632
+ case 'strong':
578
633
 
579
634
  if( opts.inFullwidth ) {
580
635
  // figure out what to do with these elements if in a fullwidth section
@@ -582,7 +637,8 @@ function generateShortcodes( mainContent, opts = {
582
637
  // these elements get added to a text module
583
638
  }else{
584
639
  // if not already opened
585
- if( ! opts.openElement ) {
640
+ // or the last opened element is a column
641
+ if( ! opts.openElement || 'et_pb_column' === opts.openElement ) {
586
642
  opts.openElement = 'et_pb_text';
587
643
 
588
644
  // this allows other elements to be added to the text modules
@@ -602,7 +658,6 @@ function generateShortcodes( mainContent, opts = {
602
658
  }
603
659
  );
604
660
 
605
-
606
661
  // a text module has already been opened so we just add to it
607
662
  }else {
608
663
  output += addElement(
@@ -617,7 +672,6 @@ function generateShortcodes( mainContent, opts = {
617
672
  break;
618
673
  // default is a string element
619
674
  default:
620
- // console.log( htmlElement)
621
675
  output += htmlElement;
622
676
  break;
623
677
 
@@ -629,30 +683,328 @@ function generateShortcodes( mainContent, opts = {
629
683
  return output;
630
684
  }
631
685
 
632
- function generateModuleShortcode(module, content ){
686
+ /**
687
+ * Bootstraps to Object conversion process.
688
+ *
689
+ * @param {*} classes
690
+ * @param {*} styles
691
+ * @returns {object}
692
+ */
693
+ function bsToProp(classes, styles = {}) {
694
+ let props = {};
695
+
696
+ let fontWeight = {
697
+ lighter: '200',
698
+ light: '300',
699
+ normal: '400',
700
+ medium: '500',
701
+ semibold: '600',
702
+ bold: '700',
703
+ extrabold: '800',
704
+ }
705
+ let fontColors = {
706
+ 'light': 'ededef',
707
+ 'dark': '3b3a48',
708
+ 'white': 'fff',
709
+ 'black': '000',
710
+ }
711
+ let alignments = {
712
+ 'left': 'left',
713
+ 'center': 'center',
714
+ 'right': 'right',
715
+ }
716
+
717
+ for( const c of classes.values() ) {
718
+ if( c.startsWith('fw-') ) {
719
+ let fontWeightValue = c.replace('fw-', '');
720
+ props.font_weight = fontWeight[fontWeightValue] || fontWeight.normal;
721
+ classes.remove(c);
722
+ }
723
+
724
+ if( c.startsWith('text-') ) {
725
+ let textUtil = c.replace('text-', '');
726
+ // text orientation
727
+ if( alignments[textUtil] ){
728
+ props.text_orientation = alignments[textUtil];
729
+ classes.remove(c);
730
+ }
731
+
732
+ // text color
733
+ if( fontColors[textUtil] ) {
734
+ props.font_color = fontColors[textUtil];
735
+ classes.remove(c);
736
+ }
737
+
738
+ }
739
+ }
740
+
741
+ if( styles && styles.length ) {
742
+ styles.split(';').filter(Boolean).forEach( (v) => {
743
+ let k = v.split(':')[0].trim();
744
+ let s = v.split(':')[1].trim();
745
+ // style mappings
746
+ switch( k ) {
747
+ case 'background-color':
748
+ props.background_color = s;
749
+ case 'background-image':
750
+ let gradient = s.replace(/.*[,\s]*linear-gradient\((.*?)\).*/g, '$1').replace(/["']/g, '');
751
+
752
+ if( gradient ) {
753
+ // if the gradient is present we want to add the appropriate values
754
+ let gradientValues = gradient.split(',');
755
+
756
+ props.background_color_gradient_direction = gradientValues[0].trim();
757
+ props.background_color_gradient_stops = gradientValues.slice(1).join('|');
758
+ props.use_background_color_gradient = 'on';
759
+ }
760
+
761
+ props.background_image = s.replace(/url\((.*?)\).*/g, '$1').replace(/["']/g, '');
762
+ break;
763
+ case 'background-repeat':
764
+ props.background_repeat = s.replace(/(.*?)(repeat|no-repeat)/g, '$2');
765
+ break;
766
+ case 'background-position':
767
+ /**
768
+ * position can be 4 different syntaxes so lets split the values
769
+ * @link https://developer.mozilla.org/en-US/docs/Web/CSS/background-position
770
+ */
771
+ let values = s.split(' ');
772
+ // for whatever reason Divi has these values inverted
773
+
774
+ switch( values.length ) {
775
+ case 1:
776
+ s = values[0];
777
+ break;
778
+ case 2:
779
+ s = `${values[1]}_${values[0]}`;
780
+ break;
781
+ case 3:
782
+ case 4:
783
+ s = `${values[2]}_${values[0]}`;
784
+ break;
785
+ }
786
+ props.background_position = s;
787
+ break;
788
+ case 'background-size':
789
+ props.background_size = s;
790
+ case 'color':
791
+ props.font_color = s;
792
+ break;
793
+ }
794
+ });
795
+ }
796
+
797
+ return props;
798
+ }
799
+
800
+ /**
801
+ * Generates a Divi module shortcode from the module name and element.
802
+ *
803
+ * @param {*} module
804
+ * @param {*} element
805
+ * @returns
806
+ */
807
+ function generateModuleShortcode(module, element ){
808
+ let content = '';
809
+ let attrs = {};
810
+ let moduleName = 'et_pb_' + module;
633
811
 
634
812
  switch( module ) {
635
- case 'blurb':
636
- // blurb module
637
- let attrs = {
638
- title: findElementByClass(content, 'card-title'),
639
- };
813
+ case 'blurb': {
814
+ /**
815
+ * if blurb module is requested
816
+ * we try and make the shortcode as if the element
817
+ * was made with a Card Component
818
+ */
819
+
820
+ let header = element.querySelector('.card-header');
821
+ let title = element.querySelector('.card-title');
822
+ let img = element.children.filter( c => c.tagName.toLowerCase() === 'img' );
823
+
824
+ content = element.querySelector('.card-body');
825
+
826
+ // the link_option_url is determined by either the
827
+ // .card-header options link or the .card-title data-url
828
+ // .card-header will take precedence over the .card-title
829
+ if( header && header.querySelector('.options a') ) {
830
+ let link = header.querySelector('.options a');
831
+ attrs.link_option_url = link.getAttribute('href');
832
+ }
833
+
834
+ // if the element has a .card-title, we want to add it to the attributes
835
+ if( title ) {
836
+ attrs.title = title.text.trim();
837
+
838
+ // if the link_option_url is not set, we want to set it to the title's data-url
839
+ if( ! attrs.link_option_url && title.getAttribute('data-url') ) {
840
+ attrs.link_option_url = title.getAttribute('data-url');
841
+ }
842
+
843
+ // text orientation
844
+ attrs.text_orientation = 'left';
640
845
 
641
- let img = findSiblingElement(content, 'img');
846
+ if( title.classList.contains('text-center') ){
847
+ attrs.text_orientation = 'center';
848
+ }else if( title.classList.contains('text-right') ){
849
+ attrs.text_orientation = 'right';
850
+ }
851
+
852
+ // remove the .card-title from the content
853
+ title.remove();
854
+ }
642
855
 
856
+ // if the element has an image, we want to add it to the attributes
643
857
  if( img ){
644
- let imgAttrs = convertRawAttrs( img.rawAttrs );
645
- attrs.image = imgAttrs.src
858
+ attrs.image = img[0].getAttribute('src')
859
+ }
860
+
861
+ // if the element has a .card-body
862
+ if( content ){
863
+ // if the content has a class of text-light,
864
+ if(content.classList.contains('text-light')){
865
+ attrs.background_color = 'light';
866
+
867
+ // background_layout opposite background_color
868
+ attrs.background_layout = 'dark';
869
+ }else{
870
+
871
+ attrs.background_color = 'dark';
872
+
873
+ // background_layout opposite background_color
874
+ attrs.background_layout = 'light';
875
+ }
876
+
877
+ content = content.innerHTML.trim();
646
878
  }
647
879
 
648
- return addElement(
649
- 'et_pb_blurb',
650
- {
651
- limiter: 'bracket',
652
- attrs
880
+ // remove the card layout class from class list
881
+ element.classList.remove('card');
882
+ element.classList.remove('blurb');
883
+ element.classList.remove('bg-transparent');
884
+ for( const c of element.classList.values() ) {
885
+ if( c.startsWith('card-') ) {
886
+ element.classList.remove(c);
653
887
  }
654
- );
888
+ }
889
+ break;
890
+ }
891
+ case 'ca_card': {
892
+ /**
893
+ * if card module is requested
894
+ */
895
+ let header = element.querySelector('.card-header');
896
+ let title = element.querySelector('.card-title');
897
+ let img = element.children.filter( c => c.tagName.toLowerCase() === 'img' );
898
+ let layout = element.classList.toString().match(/card-(\w+)/g)[0].replace('card-', '');
899
+
900
+ content = element.querySelector('.card-body');
901
+
902
+ // card layout
903
+ attrs.card_layout = layout || 'default';
904
+
905
+ // .card-header
906
+ if( header ) {
907
+ // the link_option_url is determined by either the
908
+ // .card-header options link or the .card-title data-url
909
+ if( header.querySelector('.options a') ){
910
+ let link = header.querySelector('.options a');
911
+
912
+ attrs.show_button = 'on';
913
+ attrs.button_link = link.getAttribute('href');
914
+ }
915
+
916
+ attrs.include_header = 'on';
917
+ attrs.title = header.text.trim();
918
+
919
+ }
920
+
921
+ // if the element has a .card-title
922
+ if( title ) {
923
+
924
+ // if the link_option_url is not set, we want to set it to the title's data-url
925
+ if( ! attrs.link_option_url && title.getAttribute('data-url') ) {
926
+
927
+ attrs.show_button = 'on';
928
+ attrs.button_link = link.getAttribute('href');
929
+ }
930
+ }
931
+
932
+ // if the element has an image, we want to add it to the attributes
933
+ if( img && img.length ) {
934
+ attrs.show_image = 'on';
935
+ attrs.featured_image = img[0].getAttribute('src');
936
+ }
937
+
938
+ // if the element has a .card-body
939
+ if( content ){
940
+ // if the content has a class of text-light,
941
+ if(content.classList.contains('text-light')){
942
+ attrs.background_color = 'light';
943
+
944
+ // background_layout opposite background_color
945
+ attrs.background_layout = 'dark';
946
+ }else{
947
+
948
+ attrs.background_color = 'dark';
949
+
950
+ // background_layout opposite background_color
951
+ attrs.background_layout = 'light';
952
+ }
953
+
954
+ content = content.innerHTML.trim();
955
+ }
956
+
957
+ // remove the card layout class from class list
958
+ element.classList.remove('card');
959
+ for( const c of element.classList.values() ) {
960
+ if( c.startsWith('card-') ) {
961
+ element.classList.remove(c);
962
+ }
963
+ }
964
+ break;
965
+ }
966
+ case 'heading': {
967
+
968
+ attrs = {
969
+ title: element.innerHTML.trim(),
970
+ title_level: element.tagName.toLowerCase(),
971
+ ...bsToProp(element.classList, element?._rawAttrs?.style),
972
+ };
973
+
974
+ // if there was a text orientation, it needs to be title_text_align.
975
+ if( attrs.text_orientation ) {
976
+ attrs.title_text_align = attrs.text_orientation;
977
+ delete attrs.text_orientation;
978
+ }
979
+
980
+ // if there was a font color, it needs to be title_text_color.
981
+ if( attrs.font_color ) {
982
+ attrs.title_text_color = attrs.font_color;
983
+ delete attrs.font_color;
984
+ }
985
+
986
+ content = element.innerHTML.trim()
987
+
988
+ break;
989
+ }
655
990
  }
991
+
992
+ if( element.classList.length ){
993
+ attrs.class = [...element.classList.values()].join(' ').trim();
994
+ }
995
+
996
+ // return the module shortcode
997
+ return addElement(
998
+ moduleName,
999
+ {
1000
+ limiter: 'bracket',
1001
+ attrs: {
1002
+ _builder_version: DIVI_VER,
1003
+ ...attrs
1004
+ },
1005
+ content
1006
+ }
1007
+ );
656
1008
  }
657
1009
 
658
1010
  /**
@@ -668,11 +1020,30 @@ export default async function convertSite({
668
1020
  debug} ) {
669
1021
  spinner.stop();
670
1022
 
671
- // let buildPath = path.join( appPath, 'content' );
672
1023
  let buildPath = path.join( appPath, 'build' );
1024
+ let favicon = path.join( appPath, 'build', 'favicon.ico' );
1025
+ let logo = fs.existsSync(path.join(appPath, 'build', 'media', 'logo.png') ) ?
1026
+ path.join(appPath, 'build', 'media', 'logo.png') :
1027
+ path.join(appPath, 'build', 'caweb', 'template', 'media', 'logo.png');
1028
+
1029
+ let siteData = fs.existsSync( appPath, 'caweb.json' ) ?
1030
+ JSON.parse( fs.readFileSync( path.join(appPath, 'caweb.json') ) ) :
1031
+ {
1032
+ site:{
1033
+ favicon,
1034
+ logo
1035
+ }
1036
+ };
1037
+
1038
+ let pages = [];
1039
+
1040
+ // if site data has no site object, we want to create it
1041
+ if( ! siteData.site ) {
1042
+ siteData.site = {favicon, logo};
1043
+ }
673
1044
 
674
1045
  /**
675
- * Return all .htlm files in the build directory
1046
+ * Return all .html files in the build directory
676
1047
  *
677
1048
  * exclusions:
678
1049
  * - serp.html - This a search engine results page, we don't need to parse this
@@ -684,12 +1055,13 @@ export default async function convertSite({
684
1055
  for( const file of sitePages ) {
685
1056
  // get the file path
686
1057
  let filePath = path.join( buildPath, file );
687
- let fileMarkup = fs.readFileSync( filePath, 'utf8' );
688
1058
 
689
1059
  // We use jsdom to emulate a browser environment and get the window.document
690
1060
  let fileMarkupJSon = await jsdom.JSDOM.fromFile( filePath );
691
1061
  let { document } = fileMarkupJSon.window;
692
1062
 
1063
+ validatePage( document );
1064
+
693
1065
  // all we need is the document #page-container #main-content
694
1066
  let mainContent = document.querySelector( '#page-container #main-content' );
695
1067
 
@@ -701,6 +1073,7 @@ export default async function convertSite({
701
1073
 
702
1074
  // sanitize the json
703
1075
  mainContentJson = sanitizeJson( mainContentJson.childNodes )[0];
1076
+
704
1077
  /**
705
1078
  * Main content is allowed 2 elements
706
1079
  * - main - renders the main content
@@ -719,71 +1092,54 @@ export default async function convertSite({
719
1092
  shortcodeContent += generateShortcodes( [mainContent.childNodes[e]] );
720
1093
  }
721
1094
 
722
- console.log( `shortcodeContent: ${shortcodeContent}` );
1095
+ // site info
1096
+ let slug = file.replace( '.html', '' );
1097
+ let title = slug.replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
1098
+
1099
+ pages.push({
1100
+ title,
1101
+ slug,
1102
+ shortcodeContent
1103
+ });
1104
+
723
1105
  }
724
1106
 
725
1107
 
726
1108
  }
727
1109
 
728
1110
  }
729
-
730
- return
731
- let siteData = {};
732
-
733
- // check if the site data file exists
734
- if( fs.existsSync( filePath ) ) {
735
- let file = fs.readFileSync( filePath, 'utf8' );
736
- let data = JSON.parse( file );
737
-
738
- // check if the data file has site data
739
- if( data.site ) {
740
- const continueProcess = await confirm(
741
- {
742
- message: `Site data already exists, existing data will be overwritten.\nWould you like to continue?`,
743
- default: true
744
- },
745
- {
746
- clearPromptOnDone: true,
747
- }
748
- ).catch(() => {process.exit(1);});
749
-
750
- // if the user wants to continue, set the site data
751
- // otherwise exit the process
752
- if( continueProcess ){
753
- siteData = data.site;
754
- }else{
755
- spinner.fail( 'Site creation cancelled.' );
756
- process.exit( 0 );
757
- }
758
- }
1111
+
1112
+ // if no favicon is set, we want to set the default favicon
1113
+ if( ! siteData?.favicon ){
1114
+ siteData.favicon = favicon;
759
1115
  }
760
1116
 
761
- writeLine('CAWebPublishing Site Creation Process', {char: '#', borderColor: 'green'});
762
- writeLine('This process will create a site configuration file for CAWebPublishing.', {color: 'cyan', prefix: 'i'});
763
- writeLine('Please answer the following questions to create your site configuration file.', {color: 'cyan', prefix: 'i'});
764
- writeLine('You can skip any question by pressing enter.', {color: 'cyan', prefix: 'i'});
765
- writeLine('You can also edit the configuration file later.', {color: 'cyan', prefix: 'i'});
1117
+ // if no logo is set, we want to set the default logo
1118
+ if( ! siteData?.logo ){
1119
+ siteData.logo = logo;
1120
+ }
766
1121
 
767
- // populate the site data
1122
+ // add sync entry and pages to siteData
768
1123
  siteData = {
769
- ...await promptForGeneralInfo(siteTitle),
770
- header: await promptForHeader(),
771
- alerts: await promptForAlerts(),
772
- social: await promptForSocial(),
773
- google: await promptForGoogleOptions(),
774
- footer: await promptForFooter(),
775
- };
1124
+ ...siteData,
1125
+ sync:{
1126
+ ...siteData.sync || {},
1127
+ static:{
1128
+ user: 'static',
1129
+ pwd: 'static',
1130
+ url: 'static'
1131
+ }
1132
+ },
1133
+ media: path.join( appPath, 'build', 'media' ),
1134
+ pages,
1135
+ }
776
1136
 
777
1137
  // write the site data to the file
778
1138
  fs.writeFileSync(
779
1139
  path.join( appPath, 'caweb.json' ),
780
- JSON.stringify( {site:siteData}, null, 4 )
1140
+ JSON.stringify( siteData, null, 4 )
781
1141
  );
782
-
783
- writeLine('CAWebPublishing Site Creation Process Complete', {char: '#', borderColor: 'green'});
784
- writeLine('You can now start the site by running the following command:', {color: 'cyan', prefix: 'i'});
785
- writeLine(`npm run caweb serve`, {color: 'cyan', prefix: 'i'});
786
-
787
- spinner.start('CAWebPublishing Site Configuration file saved.');
1142
+
1143
+ writeLine( chalk.green( 'Site converted successfully.' ) );
788
1144
 
789
1145
  };