@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.
- package/commands/env/start.js +67 -68
- package/commands/gen-scripts.js +66 -0
- package/commands/index.js +12 -9
- package/commands/sites/convert-site.js +508 -152
- package/commands/sites/create-site.js +25 -3
- package/commands/sites/prompts.js +34 -15
- package/commands/sites/validation.js +68 -0
- package/commands/sync/index.js +530 -239
- package/configs/prompts.js +64 -0
- package/configs/wp-env.js +114 -25
- package/lib/cli.js +84 -72
- package/lib/wordpress/api.js +22 -13
- package/lib/wordpress/caweb.js +6 -20
- package/lib/wordpress/download-sources.js +38 -16
- package/lib/wordpress/options.js +21 -18
- package/lib/wordpress/wordpress.js +105 -30
- package/package.json +12 -7
- package/commands/env/destroy.js +0 -49
- package/commands/env/stop.js +0 -35
- package/configs/docker-compose.js +0 -132
- /package/commands/{tasks → env}/shell.js +0 -0
- /package/commands/{tasks → env}/update-plugins.js +0 -0
|
@@ -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': [
|
|
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"
|
|
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
|
-
|
|
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 = '
|
|
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
|
-
|
|
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'
|
|
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
|
-
'
|
|
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
|
|
465
|
-
case
|
|
466
|
-
colType = '
|
|
493
|
+
// 1/4 columns
|
|
494
|
+
case 3:
|
|
495
|
+
colType = '1_4';
|
|
467
496
|
break;
|
|
468
|
-
//
|
|
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
|
|
474
|
-
colType = '
|
|
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')
|
|
517
|
-
// this is
|
|
518
|
-
|
|
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:
|
|
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
|
-
|
|
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 +=
|
|
552
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
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
|
-
|
|
645
|
-
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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 .
|
|
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
|
-
|
|
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
|
-
|
|
731
|
-
|
|
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
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
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
|
-
//
|
|
1122
|
+
// add sync entry and pages to siteData
|
|
768
1123
|
siteData = {
|
|
769
|
-
...
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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(
|
|
1140
|
+
JSON.stringify( siteData, null, 4 )
|
|
781
1141
|
);
|
|
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.');
|
|
1142
|
+
|
|
1143
|
+
writeLine( chalk.green( 'Site converted successfully.' ) );
|
|
788
1144
|
|
|
789
1145
|
};
|