@caweb/cli 1.11.1 → 1.11.2
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/commands/sites/convert-site.js +332 -129
- package/commands/sites/validation.js +68 -0
- package/commands/sync/index.js +484 -234
- package/lib/wordpress/api.js +4 -3
- package/package.json +6 -5
|
@@ -3,29 +3,22 @@
|
|
|
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
|
*/
|
|
@@ -57,37 +50,6 @@ function getLimiter( type ) {
|
|
|
57
50
|
return limiter;
|
|
58
51
|
}
|
|
59
52
|
|
|
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
53
|
/**
|
|
92
54
|
* This function is used to sanitize the JSON data.
|
|
93
55
|
*
|
|
@@ -188,15 +150,58 @@ function getAttrString(attributes, limiter) {
|
|
|
188
150
|
|
|
189
151
|
// style mappings
|
|
190
152
|
switch( k ) {
|
|
153
|
+
case 'background-color':
|
|
154
|
+
k = 'background_color';
|
|
155
|
+
break;
|
|
191
156
|
case 'background-image':
|
|
192
157
|
k = 'background_image';
|
|
193
|
-
|
|
158
|
+
|
|
159
|
+
let gradient = s.replace(/.*[,\s]*linear-gradient\((.*?)\).*/g, '$1').replace(/["']/g, '');
|
|
160
|
+
|
|
161
|
+
if( gradient ) {
|
|
162
|
+
// if the gradient is present we want to add the appropriate values
|
|
163
|
+
let gradientValues = gradient.split(',');
|
|
164
|
+
|
|
165
|
+
key.push(`background_color_gradient_direction="${gradientValues[0].trim()}"`);
|
|
166
|
+
key.push(`background_color_gradient_stops="${gradientValues.slice(1).join('|')}"`);
|
|
167
|
+
key.push('use_background_color_gradient="on"');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
s = s.replace(/url\((.*?)\).*/g, '$1').replace(/["']/g, '');
|
|
194
171
|
break;
|
|
195
172
|
|
|
196
173
|
case 'background-repeat':
|
|
197
|
-
k = '
|
|
174
|
+
k = 'background_repeat';
|
|
198
175
|
s = s.replace(/(.*?)(repeat|no-repeat)/g, '$2');
|
|
199
176
|
break;
|
|
177
|
+
|
|
178
|
+
case 'background-position':
|
|
179
|
+
k = 'background_position';
|
|
180
|
+
/**
|
|
181
|
+
* position can be 4 different syntaxes so lets split the values
|
|
182
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/background-position
|
|
183
|
+
*/
|
|
184
|
+
let values = s.split(' ');
|
|
185
|
+
// for whatever reason Divi has these values inverted
|
|
186
|
+
|
|
187
|
+
switch( values.length ) {
|
|
188
|
+
case 1:
|
|
189
|
+
s = values[0];
|
|
190
|
+
break;
|
|
191
|
+
case 2:
|
|
192
|
+
s = `${values[1]}_${values[0]}`;
|
|
193
|
+
break;
|
|
194
|
+
case 3:
|
|
195
|
+
case 4:
|
|
196
|
+
s = `${values[2]}_${values[0]}`;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'background-size':
|
|
202
|
+
k = 'background_size';
|
|
203
|
+
|
|
204
|
+
break;
|
|
200
205
|
default:
|
|
201
206
|
k = '';
|
|
202
207
|
break;
|
|
@@ -210,8 +215,11 @@ function getAttrString(attributes, limiter) {
|
|
|
210
215
|
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
|
-
|
|
218
|
+
|
|
214
219
|
if( '' !== key && 'string' === typeof key ) {
|
|
220
|
+
// we have to encode certain characters
|
|
221
|
+
value = value.replaceAll('"', '%22');
|
|
222
|
+
|
|
215
223
|
attrString += ` ${key}="${value}"`;
|
|
216
224
|
}else if( 'object' === typeof key ) {
|
|
217
225
|
attrString += ` ${key.join(' ')}`;
|
|
@@ -278,6 +286,7 @@ function closeElement( tag, limiter ) {
|
|
|
278
286
|
return `${lmtrObj.open}/${tag}${lmtrObj.close}`
|
|
279
287
|
}
|
|
280
288
|
|
|
289
|
+
|
|
281
290
|
/**
|
|
282
291
|
* Generates shortcodes from the json object.
|
|
283
292
|
* Converts a json dom object into shortcodes.
|
|
@@ -406,7 +415,10 @@ function generateShortcodes( mainContent, opts = {
|
|
|
406
415
|
{
|
|
407
416
|
limiter:opts.limiter,
|
|
408
417
|
content: generateShortcodes(content, opts),
|
|
409
|
-
attrs
|
|
418
|
+
attrs: {
|
|
419
|
+
_builder_version: DIVI_VER,
|
|
420
|
+
...attrs,
|
|
421
|
+
}
|
|
410
422
|
}
|
|
411
423
|
);
|
|
412
424
|
|
|
@@ -513,9 +525,14 @@ function generateShortcodes( mainContent, opts = {
|
|
|
513
525
|
if( opts.inFullwidth ) {
|
|
514
526
|
// output += addElement('', { limiter: 'none', content: generateShortcodes(content) });
|
|
515
527
|
}else{
|
|
516
|
-
if( classes.includes('card')
|
|
517
|
-
// this is
|
|
518
|
-
|
|
528
|
+
if( classes.includes('card') ){
|
|
529
|
+
// this is Divi blurb module
|
|
530
|
+
if( classes.includes('blurb') ) {
|
|
531
|
+
output += generateModuleShortcode('blurb', htmlElement );
|
|
532
|
+
}else{
|
|
533
|
+
// this is our card module
|
|
534
|
+
output += generateModuleShortcode('ca_card', htmlElement );
|
|
535
|
+
}
|
|
519
536
|
|
|
520
537
|
}else{
|
|
521
538
|
// figure out what kind of div element we are dealing with
|
|
@@ -546,17 +563,11 @@ function generateShortcodes( mainContent, opts = {
|
|
|
546
563
|
case 'h6':
|
|
547
564
|
// we let the h1-h6 process know element is opened
|
|
548
565
|
// this allows other elements to be added to the header modules
|
|
549
|
-
opts.openElement = 'et_pb_heading';
|
|
566
|
+
// opts.openElement = 'et_pb_heading';
|
|
550
567
|
|
|
551
|
-
output += addElement(
|
|
552
|
-
opts.openElement,
|
|
553
|
-
{
|
|
554
|
-
limiter: opts.limiter,
|
|
555
|
-
content: generateShortcodes(content, opts)
|
|
556
|
-
}
|
|
557
|
-
);
|
|
558
568
|
|
|
559
|
-
|
|
569
|
+
output += generateModuleShortcode('heading', htmlElement);
|
|
570
|
+
// opts.openElement = false;
|
|
560
571
|
break;
|
|
561
572
|
|
|
562
573
|
case 'img':
|
|
@@ -582,7 +593,8 @@ function generateShortcodes( mainContent, opts = {
|
|
|
582
593
|
// these elements get added to a text module
|
|
583
594
|
}else{
|
|
584
595
|
// if not already opened
|
|
585
|
-
|
|
596
|
+
// or the last opened element is a column
|
|
597
|
+
if( ! opts.openElement || 'et_pb_column' === opts.openElement ) {
|
|
586
598
|
opts.openElement = 'et_pb_text';
|
|
587
599
|
|
|
588
600
|
// this allows other elements to be added to the text modules
|
|
@@ -629,30 +641,245 @@ function generateShortcodes( mainContent, opts = {
|
|
|
629
641
|
return output;
|
|
630
642
|
}
|
|
631
643
|
|
|
632
|
-
function generateModuleShortcode(module,
|
|
644
|
+
function generateModuleShortcode(module, element ){
|
|
645
|
+
let content = '';
|
|
646
|
+
let attrs = {};
|
|
647
|
+
let moduleName = 'et_pb_' + module;
|
|
633
648
|
|
|
634
649
|
switch( module ) {
|
|
635
|
-
case 'blurb':
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
650
|
+
case 'blurb': {
|
|
651
|
+
/**
|
|
652
|
+
* if blurb module is requested
|
|
653
|
+
* we try and make the shortcode as if the element
|
|
654
|
+
* was made with a Card Component
|
|
655
|
+
*/
|
|
656
|
+
|
|
657
|
+
let header = element.querySelector('.card-header');
|
|
658
|
+
let title = element.querySelector('.card-title');
|
|
659
|
+
let img = element.children.filter( c => c.tagName.toLowerCase() === 'img' );
|
|
660
|
+
|
|
661
|
+
content = element.querySelector('.card-body');
|
|
662
|
+
|
|
663
|
+
// the link_option_url is determined by either the
|
|
664
|
+
// .card-header options link or the .card-title data-url
|
|
665
|
+
// .card-header will take precedence over the .card-title
|
|
666
|
+
if( header && header.querySelector('.options a') ) {
|
|
667
|
+
let link = header.querySelector('.options a');
|
|
668
|
+
attrs.link_option_url = link.getAttribute('href');
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// if the element has a .card-title, we want to add it to the attributes
|
|
672
|
+
if( title ) {
|
|
673
|
+
attrs.title = title.text.trim();
|
|
674
|
+
|
|
675
|
+
// if the link_option_url is not set, we want to set it to the title's data-url
|
|
676
|
+
if( ! attrs.link_option_url && title.getAttribute('data-url') ) {
|
|
677
|
+
attrs.link_option_url = title.getAttribute('data-url');
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// text orientation
|
|
681
|
+
attrs.text_orientation = 'left';
|
|
682
|
+
|
|
683
|
+
if( title.classList.contains('text-center') ){
|
|
684
|
+
attrs.text_orientation = 'center';
|
|
685
|
+
}else if( title.classList.contains('text-right') ){
|
|
686
|
+
attrs.text_orientation = 'right';
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// remove the .card-title from the content
|
|
690
|
+
title.remove();
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// if the element has an image, we want to add it to the attributes
|
|
694
|
+
if( img ){
|
|
695
|
+
attrs.image = img[0].getAttribute('src')
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// if the element has a .card-body
|
|
699
|
+
if( content ){
|
|
700
|
+
// if the content has a class of text-light,
|
|
701
|
+
if(content.classList.contains('text-light')){
|
|
702
|
+
attrs.background_color = 'light';
|
|
703
|
+
|
|
704
|
+
// background_layout opposite background_color
|
|
705
|
+
attrs.background_layout = 'dark';
|
|
706
|
+
}else{
|
|
707
|
+
|
|
708
|
+
attrs.background_color = 'dark';
|
|
709
|
+
|
|
710
|
+
// background_layout opposite background_color
|
|
711
|
+
attrs.background_layout = 'light';
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
content = content.innerHTML.trim();
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// remove the card layout class from class list
|
|
718
|
+
element.classList.remove('card');
|
|
719
|
+
element.classList.remove('blurb');
|
|
720
|
+
element.classList.remove('bg-transparent');
|
|
721
|
+
for( const c of element.classList.values() ) {
|
|
722
|
+
if( c.startsWith('card-') ) {
|
|
723
|
+
element.classList.remove(c);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
case 'ca_card': {
|
|
729
|
+
/**
|
|
730
|
+
* if card module is requested
|
|
731
|
+
*/
|
|
732
|
+
let header = element.querySelector('.card-header');
|
|
733
|
+
let title = element.querySelector('.card-title');
|
|
734
|
+
let img = element.children.filter( c => c.tagName.toLowerCase() === 'img' );
|
|
735
|
+
let layout = element.classList.toString().match(/card-(\w+)/g)[0].replace('card-', '');
|
|
736
|
+
|
|
737
|
+
content = element.querySelector('.card-body');
|
|
738
|
+
|
|
739
|
+
// card layout
|
|
740
|
+
attrs.card_layout = layout || 'default';
|
|
741
|
+
|
|
742
|
+
// .card-header
|
|
743
|
+
if( header ) {
|
|
744
|
+
// the link_option_url is determined by either the
|
|
745
|
+
// .card-header options link or the .card-title data-url
|
|
746
|
+
if( header.querySelector('.options a') ){
|
|
747
|
+
let link = header.querySelector('.options a');
|
|
748
|
+
|
|
749
|
+
attrs.show_button = 'on';
|
|
750
|
+
attrs.button_link = link.getAttribute('href');
|
|
751
|
+
}
|
|
640
752
|
|
|
641
|
-
|
|
753
|
+
attrs.include_header = 'on';
|
|
754
|
+
attrs.title = header.text.trim();
|
|
755
|
+
|
|
756
|
+
}
|
|
642
757
|
|
|
758
|
+
// if the element has a .card-title
|
|
759
|
+
if( title ) {
|
|
760
|
+
|
|
761
|
+
// if the link_option_url is not set, we want to set it to the title's data-url
|
|
762
|
+
if( ! attrs.link_option_url && title.getAttribute('data-url') ) {
|
|
763
|
+
|
|
764
|
+
attrs.show_button = 'on';
|
|
765
|
+
attrs.button_link = link.getAttribute('href');
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// if the element has an image, we want to add it to the attributes
|
|
643
770
|
if( img ){
|
|
644
|
-
|
|
645
|
-
attrs.
|
|
771
|
+
attrs.show_image = 'on';
|
|
772
|
+
attrs.featured_image = img[0].getAttribute('src');
|
|
646
773
|
}
|
|
647
774
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
attrs
|
|
775
|
+
// if the element has a .card-body
|
|
776
|
+
if( content ){
|
|
777
|
+
// if the content has a class of text-light,
|
|
778
|
+
if(content.classList.contains('text-light')){
|
|
779
|
+
attrs.background_color = 'light';
|
|
780
|
+
|
|
781
|
+
// background_layout opposite background_color
|
|
782
|
+
attrs.background_layout = 'dark';
|
|
783
|
+
}else{
|
|
784
|
+
|
|
785
|
+
attrs.background_color = 'dark';
|
|
786
|
+
|
|
787
|
+
// background_layout opposite background_color
|
|
788
|
+
attrs.background_layout = 'light';
|
|
653
789
|
}
|
|
654
|
-
|
|
790
|
+
|
|
791
|
+
content = content.innerHTML.trim();
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// remove the card layout class from class list
|
|
795
|
+
element.classList.remove('card');
|
|
796
|
+
for( const c of element.classList.values() ) {
|
|
797
|
+
if( c.startsWith('card-') ) {
|
|
798
|
+
element.classList.remove(c);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
case 'heading': {
|
|
804
|
+
let title_font = [];
|
|
805
|
+
let title_text_color = '';
|
|
806
|
+
|
|
807
|
+
attrs = {
|
|
808
|
+
title: element.innerHTML.trim(),
|
|
809
|
+
title_level: element.tagName.toLowerCase()
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
for( const c of element.classList.values() ) {
|
|
813
|
+
// font weight classes
|
|
814
|
+
if( c.startsWith('fw-') ) {
|
|
815
|
+
let fontWeight = {
|
|
816
|
+
lighter: '200',
|
|
817
|
+
light: '300',
|
|
818
|
+
normal: '400',
|
|
819
|
+
medium: '500',
|
|
820
|
+
semibold: '600',
|
|
821
|
+
bold: '700',
|
|
822
|
+
extrabold: '800',
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
let fontWeightValue = c.replace('fw-', '');
|
|
827
|
+
|
|
828
|
+
title_font[1] = fontWeight[fontWeightValue] || fontWeight.normal;
|
|
829
|
+
|
|
830
|
+
// remove the class from the class list
|
|
831
|
+
element.classList.remove(c);
|
|
832
|
+
// text classes
|
|
833
|
+
}else if( c.startsWith('text-') ) {
|
|
834
|
+
let fontColors = {
|
|
835
|
+
'light': 'ededef',
|
|
836
|
+
'dark': '3b3a48',
|
|
837
|
+
'white': 'fff',
|
|
838
|
+
'black': '000',
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
let fontColorValue = c.replace('text-', '');
|
|
842
|
+
|
|
843
|
+
if( fontColors[fontColorValue] ) {
|
|
844
|
+
title_text_color = `#${fontColors[fontColorValue]}`;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
// remove the class from the class list
|
|
849
|
+
element.classList.remove(c);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if( title_font.length ) {
|
|
854
|
+
attrs.title_font = title_font.join('|');
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if( title_text_color ) {
|
|
858
|
+
attrs.title_text_color = title_text_color;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
content = element.innerHTML.trim()
|
|
862
|
+
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
if( element.classList.length ){
|
|
868
|
+
attrs.class = [...element.classList.values()].join(' ').trim();
|
|
655
869
|
}
|
|
870
|
+
|
|
871
|
+
// return the module shortcode
|
|
872
|
+
return addElement(
|
|
873
|
+
moduleName,
|
|
874
|
+
{
|
|
875
|
+
limiter: 'bracket',
|
|
876
|
+
attrs: {
|
|
877
|
+
_builder_version: DIVI_VER,
|
|
878
|
+
...attrs
|
|
879
|
+
},
|
|
880
|
+
content
|
|
881
|
+
}
|
|
882
|
+
);
|
|
656
883
|
}
|
|
657
884
|
|
|
658
885
|
/**
|
|
@@ -668,11 +895,12 @@ export default async function convertSite({
|
|
|
668
895
|
debug} ) {
|
|
669
896
|
spinner.stop();
|
|
670
897
|
|
|
671
|
-
// let buildPath = path.join( appPath, 'content' );
|
|
672
898
|
let buildPath = path.join( appPath, 'build' );
|
|
899
|
+
let siteData = fs.existsSync( appPath, 'caweb.json' ) ? JSON.parse( fs.readFileSync( path.join(appPath, 'caweb.json') ) ) : {};
|
|
900
|
+
let pages = [];
|
|
673
901
|
|
|
674
902
|
/**
|
|
675
|
-
* Return all .
|
|
903
|
+
* Return all .html files in the build directory
|
|
676
904
|
*
|
|
677
905
|
* exclusions:
|
|
678
906
|
* - serp.html - This a search engine results page, we don't need to parse this
|
|
@@ -684,12 +912,13 @@ export default async function convertSite({
|
|
|
684
912
|
for( const file of sitePages ) {
|
|
685
913
|
// get the file path
|
|
686
914
|
let filePath = path.join( buildPath, file );
|
|
687
|
-
let fileMarkup = fs.readFileSync( filePath, 'utf8' );
|
|
688
915
|
|
|
689
916
|
// We use jsdom to emulate a browser environment and get the window.document
|
|
690
917
|
let fileMarkupJSon = await jsdom.JSDOM.fromFile( filePath );
|
|
691
918
|
let { document } = fileMarkupJSon.window;
|
|
692
919
|
|
|
920
|
+
validatePage( document );
|
|
921
|
+
|
|
693
922
|
// all we need is the document #page-container #main-content
|
|
694
923
|
let mainContent = document.querySelector( '#page-container #main-content' );
|
|
695
924
|
|
|
@@ -701,6 +930,7 @@ export default async function convertSite({
|
|
|
701
930
|
|
|
702
931
|
// sanitize the json
|
|
703
932
|
mainContentJson = sanitizeJson( mainContentJson.childNodes )[0];
|
|
933
|
+
|
|
704
934
|
/**
|
|
705
935
|
* Main content is allowed 2 elements
|
|
706
936
|
* - main - renders the main content
|
|
@@ -719,7 +949,16 @@ export default async function convertSite({
|
|
|
719
949
|
shortcodeContent += generateShortcodes( [mainContent.childNodes[e]] );
|
|
720
950
|
}
|
|
721
951
|
|
|
722
|
-
|
|
952
|
+
// site info
|
|
953
|
+
let slug = file.replace( '.html', '' );
|
|
954
|
+
let title = slug.replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
955
|
+
|
|
956
|
+
pages.push({
|
|
957
|
+
title,
|
|
958
|
+
slug,
|
|
959
|
+
shortcodeContent
|
|
960
|
+
});
|
|
961
|
+
|
|
723
962
|
}
|
|
724
963
|
|
|
725
964
|
|
|
@@ -727,63 +966,27 @@ export default async function convertSite({
|
|
|
727
966
|
|
|
728
967
|
}
|
|
729
968
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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 );
|
|
969
|
+
// add sync entry and pages to siteData
|
|
970
|
+
siteData = {
|
|
971
|
+
...siteData,
|
|
972
|
+
sync:{
|
|
973
|
+
...siteData.sync || {},
|
|
974
|
+
static:{
|
|
975
|
+
user: 'static',
|
|
976
|
+
pwd: 'static',
|
|
977
|
+
url: 'static'
|
|
757
978
|
}
|
|
758
|
-
}
|
|
979
|
+
},
|
|
980
|
+
media: path.join( appPath, 'build', 'media' ),
|
|
981
|
+
pages,
|
|
759
982
|
}
|
|
760
983
|
|
|
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'});
|
|
766
|
-
|
|
767
|
-
// populate the site data
|
|
768
|
-
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
|
-
};
|
|
776
|
-
|
|
777
984
|
// write the site data to the file
|
|
778
985
|
fs.writeFileSync(
|
|
779
986
|
path.join( appPath, 'caweb.json' ),
|
|
780
|
-
JSON.stringify(
|
|
987
|
+
JSON.stringify( siteData, null, 4 )
|
|
781
988
|
);
|
|
782
|
-
|
|
783
|
-
writeLine(
|
|
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.');
|
|
989
|
+
|
|
990
|
+
writeLine( chalk.green( 'Site converted successfully.' ) );
|
|
788
991
|
|
|
789
992
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { parse } from 'node-html-parser';
|
|
2
|
+
import { writeLine } from '../../lib/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validates the HTML of a page.
|
|
6
|
+
* @param {JSDOM} document - Window document from JSDOM.
|
|
7
|
+
* @returns {boolean} - Returns true if the HTML is valid, false otherwise.
|
|
8
|
+
*/
|
|
9
|
+
function validatePage( document ){
|
|
10
|
+
let mainContent = document.querySelector( '#page-container #main-content' );
|
|
11
|
+
|
|
12
|
+
// Check if document doesn't have a #page-container #main-content
|
|
13
|
+
if( ! mainContent ){
|
|
14
|
+
throw new Error("The page does not have a #page-container #main-content.");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let mainContentJson = parse( mainContent.innerHTML );
|
|
18
|
+
let mainJson = ''
|
|
19
|
+
let asideJson = '';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Main content is allowed 2 elements
|
|
23
|
+
* - main - renders the main content
|
|
24
|
+
* - aside - renders the sidebar content
|
|
25
|
+
*/
|
|
26
|
+
for( const element of mainContentJson.children ){
|
|
27
|
+
if( element.tagName !== 'MAIN' && element.tagName !== 'ASIDE' ){
|
|
28
|
+
throw new Error("The main content must only contain <main> and <aside> elements.");
|
|
29
|
+
}else if( element.tagName === 'MAIN' ){
|
|
30
|
+
mainJson = element
|
|
31
|
+
}else if( element.tagName === 'ASIDE' ){
|
|
32
|
+
asideJson = element
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Main should only consist of <div> elements.
|
|
38
|
+
* - .container-fluid - Used for full width containers.
|
|
39
|
+
* - .container - used for standard containers.
|
|
40
|
+
* -- Should only consist of div.row
|
|
41
|
+
* ---- Should only consist of div.col-#
|
|
42
|
+
* ------ All content should be in a column
|
|
43
|
+
*/
|
|
44
|
+
for( const element of mainJson.children ){
|
|
45
|
+
if( element.tagName !== 'DIV' ){
|
|
46
|
+
throw new Error("The <main> element must only contain <div> elements.");
|
|
47
|
+
}else if( ! element.classList.contains( 'container-fluid' ) && ! element.classList.contains( 'container' ) ){
|
|
48
|
+
throw new Error("The <main> element must only contain <div> elements with class 'container-fluid' or 'container'.");
|
|
49
|
+
}else if( element.classList.contains( 'container' ) ){
|
|
50
|
+
for( const row of element.children ){
|
|
51
|
+
if( row.tagName !== 'DIV' || ! row.classList.contains( 'row' ) ){
|
|
52
|
+
throw new Error("The <div> element with class 'container' must only contain <div> elements with class 'row'.");
|
|
53
|
+
}else{
|
|
54
|
+
for( const col of row.children ){
|
|
55
|
+
if( col.tagName !== 'DIV' || ! col.classList.toString().match( /col[-\d]*/g ) ){
|
|
56
|
+
throw new Error("The <div> element with class 'row' must only contain <div> elements with class 'col-#'.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default validatePage;
|