html-to-gutenberg 4.2.4 → 4.2.5

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.
Files changed (2) hide show
  1. package/index.js +287 -344
  2. package/package.json +3 -3
package/index.js CHANGED
@@ -1,12 +1,10 @@
1
1
  import presetReact from '@babel/preset-react';
2
- import { transform } from '@svgr/core';
3
2
  import * as babel from '@babel/core';
4
3
  import * as cheerio from 'cheerio';
5
4
  import scopeCss from 'css-scoping';
6
5
  import extractAssets from 'fetch-page-assets';
7
6
  import fs from 'fs';
8
7
  import icon from 'html-screenshots';
9
- import imageToBase64 from 'image-to-base64';
10
8
  import { createRequire } from 'module';
11
9
  import convert from 'node-html-to-jsx';
12
10
  import path from 'path';
@@ -52,125 +50,93 @@ const block = async (
52
50
 
53
51
  return jsFiles.some(url => tailwindCdnRegex.test(url));
54
52
  }
55
-
56
- function parseInlineStyle(styleString) {
57
- if (!styleString || typeof styleString !== 'string') return '';
58
-
59
- const styleEntries = styleString
60
- .split(';')
61
- .map(rule => rule.trim())
62
- .filter(Boolean)
63
- .map(rule => {
64
- const [property, value] = rule.split(':').map(part => part.trim());
65
-
66
- if (!property || !value) return null;
67
-
68
- const camelCasedProperty = property.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
69
-
70
- return [camelCasedProperty, value];
71
- })
72
- .filter(Boolean);
73
-
74
- const styleObject = Object.fromEntries(styleEntries);
75
-
76
- return JSON.stringify(styleObject).replace(/"([^"]+)":/g, '$1:');
53
+
54
+ function replaceSourceUrlVars(str, source) {
55
+ if (!source) return str;
56
+
57
+ const escapedSource = source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
+ const pattern = new RegExp(`var\.url\+'${escapedSource}([^']+)'`, 'g');
59
+ return str.replace(pattern, (match, path) => `\${vars.url}${path}`);
77
60
  }
78
61
 
79
- function sanitizeAndReplaceLeadingNumbers(input) {
62
+ function sanitizeAndReplaceLeadingNumbers(str) {
80
63
  const numberWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
81
- let hasReplacedFirstNumber = false;
64
+ let firstNumberReplaced = false;
82
65
 
83
- return input
66
+ return str
84
67
  .toLowerCase()
85
68
  .replace(/[\s\-_]/g, '')
86
69
  .replace(/\d/g, (digit) => {
87
- if (hasReplacedFirstNumber) return digit;
88
-
89
- hasReplacedFirstNumber = true;
90
- return numberWords[parseInt(digit)] + digit;
70
+ if (!firstNumberReplaced) {
71
+ firstNumberReplaced = true;
72
+
73
+ return numberWords[parseInt(digit)] + digit;
74
+ }
75
+ return digit;
91
76
  })
92
77
  .replace(/^[^a-z]+/, '');
93
- }
78
+ }
94
79
 
95
- const replaceUnderscoresSpacesAndUppercaseLetters = (input = '') => {
96
- const nonWordOrUnderscore = /\W|_/g;
97
- return input.replace(nonWordOrUnderscore, '-').toLowerCase();
98
- };
99
-
100
- const buildFilePath = (basePath, fileName) => {
101
- return path.join(basePath, fileName);
80
+ const replaceUnderscoresSpacesAndUppercaseLetters = (name = '') => {
81
+ return name.replace(new RegExp(/\W|_/, 'g'), '-').toLowerCase();
102
82
  };
103
-
104
- const writeFile = (filePath, data) => {
105
- fs.writeFileSync(filePath, data);
106
- };
107
83
 
108
84
  const saveFile = (fileName, contents, options) => {
109
85
  try {
110
- const filePath = buildFilePath(options.basePath, fileName);
111
- writeFile(filePath, contents);
86
+ const filePath = path.join(options.basePath, fileName);
87
+
88
+ fs.writeFileSync(filePath, contents);
89
+
112
90
  return contents;
113
91
  } catch (error) {
114
92
  logError(error);
115
93
  }
116
- };
117
-
118
- function buildRelativeUrlRegex() {
119
- const attributes = [
120
- 'src',
121
- 'href',
122
- 'action',
123
- 'srcset',
124
- 'poster',
125
- 'data',
126
- 'formaction'
94
+ };
95
+
96
+ function replaceRelativeUrls(html, replacer) {
97
+ const urlAttributes = [
98
+ 'src', 'href', 'action', 'srcset', 'poster', 'data', 'formaction'
127
99
  ];
128
100
 
129
- const protocolsToIgnore = ['https?:', '//', 'mailto:', 'tel:', '#'].join('|');
130
-
131
- return new RegExp(
132
- `\\b(${attributes.join('|')}|data-[a-zA-Z0-9_-]+)\\s*=\\s*(['"])(?!${protocolsToIgnore})([^'"]+)\\2`,
101
+ const regex = new RegExp(
102
+ `\\b(${urlAttributes.join('|')}|data-[a-zA-Z0-9_-]+)\\s*=\\s*(['"])(?!https?:|//|mailto:|tel:|#)([^'"]+)\\2`,
133
103
  'gi'
134
104
  );
135
- }
136
105
 
137
- function replaceRelativeUrls(html, replacer) {
138
- const regex = buildRelativeUrlRegex();
139
- return html.replace(regex, (_match, attribute, quote, url) => {
140
- const updatedUrl = replacer(url);
141
- return `${attribute}=${quote}${updatedUrl}${quote}`;
106
+ return html.replace(regex, (_match, attr, quote, url) => {
107
+ const newUrl = replacer(url);
108
+ return `${attr}=${quote}${newUrl}${quote}`;
142
109
  });
143
110
  }
144
-
145
- function getRelativeUrlCssRegex() {
146
- const ignoredProtocols = ['https?:', '//', 'data:', 'mailto:', 'tel:', '#'].join('|');
147
- return new RegExp(
148
- `url\\(\\s*(['"]?)(?!${ignoredProtocols})([^'")]+)\\1\\s*\\)`,
149
- 'gi'
150
- );
151
- }
111
+
152
112
 
153
113
  function replaceRelativeUrlsInCss(css, replacer) {
154
- const regex = getRelativeUrlCssRegex();
155
-
156
- return css.replace(regex, (_match, quote, url) => {
157
- const updatedUrl = replacer(url);
158
- return `url(${quote}${updatedUrl}${quote})`;
159
- });
160
- }
114
+ const regex = /url\(\s*(['"]?)(.+?)\1\s*\)/gi;
161
115
 
162
- function resolveUrl(relativePath, baseUrl) {
163
- return new URL(relativePath, baseUrl).href;
116
+ return css.replace(regex, (match, quote, url) => {
117
+ if (/^(https?:|\/\/|data:|mailto:|tel:|#)/.test(url.trim())) {
118
+ return match;
119
+ }
120
+ const newUrl = replacer(url);
121
+ return `url(${quote}${newUrl}${quote})`;
122
+ });
164
123
  }
165
124
 
166
125
  function replaceRelativeUrlsInHtml(html, baseUrl) {
167
- return replaceRelativeUrls(html, (url) => resolveUrl(url, baseUrl));
126
+ return replaceRelativeUrls(html, (url) => {
127
+ return new URL(url, baseUrl).href;
128
+ });
168
129
  }
169
-
130
+
170
131
  function replaceRelativeUrlsInCssWithBase(css, cssFileUrl) {
171
- return replaceRelativeUrlsInCss(css, (url) => resolveUrl(url, cssFileUrl));
172
- }
173
-
132
+ return replaceRelativeUrlsInCss(css, (url) => {
133
+ if (/^(https?:|\/\/|data:|mailto:|tel:|#)/.test(url.trim())) {
134
+ return url;
135
+ }
136
+ return new URL(url, cssFileUrl).href;
137
+ });
138
+ }
139
+
174
140
  const parseRequirements = async (files, options) => {
175
141
  const { source } = options;
176
142
  let output = '';
@@ -198,14 +164,13 @@ const block = async (
198
164
  return output;
199
165
  };
200
166
 
201
- const convertDashesSpacesAndUppercaseToUnderscoresAndLowercase = (input = '') => {
202
- if (!input) return '';
203
-
204
- return input
205
- .replaceAll('-', '_')
206
- .replaceAll(' ', '_')
207
- .toLowerCase();
208
- };
167
+ const convertDashesSpacesAndUppercaseToUnderscoresAndLowercase = (string) => {
168
+ if (string) {
169
+ return `${string.replaceAll('-', '_').replaceAll(' ', '_').toLowerCase()}`;
170
+ }
171
+
172
+ return '';
173
+ };
209
174
 
210
175
  const newName = replaceUnderscoresSpacesAndUppercaseLetters(name);
211
176
  const blockName = `${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(prefix))}/${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(name))}`;
@@ -420,14 +385,39 @@ const block = async (
420
385
  `;
421
386
  };
422
387
 
423
- function transformBlockFile(code) {
424
- const transformOptions = {
388
+ function preprocessSvgAttributes(code) {
389
+ return code.replace(/(<svg[\s\S]*?>[\s\S]*?<\/svg>)/gi, (svgBlock) => {
390
+ let processed = svgBlock.replace(/([a-zA-Z0-9]+)-([a-zA-Z0-9]+)=/g, (match, p1, p2) => {
391
+ const camel = p1 + p2.charAt(0).toUpperCase() + p2.slice(1);
392
+ return camel + '=';
393
+ });
394
+ return processed;
395
+ });
396
+ }
397
+
398
+ function unwrapBody(code) {
399
+ try {
400
+ return code.replace(/<\/?(html|body)[^>]*>/gi, '');
401
+ } catch (e) {
402
+ return code;
403
+ }
404
+ }
405
+
406
+ function transformBlockFile(blockCode) {
407
+ let test = '';
408
+
409
+ try {
410
+ test = babel.transformSync(blockCode, {
425
411
  presets: [[presetReact, { pragma: 'wp.element.createElement' }]],
426
- filename: 'block.js',
427
- };
428
-
429
- return babel.transformSync(code, transformOptions);
430
- }
412
+ filename: 'block.js'
413
+ });
414
+ } catch (error) {
415
+ console.log(error);
416
+
417
+ }
418
+
419
+ return test;
420
+ }
431
421
 
432
422
  const saveFiles = async (options) => {
433
423
  const { cssFiles = [], jsFiles = [], shouldSaveFiles, name, prefix } = options;
@@ -442,33 +432,57 @@ const block = async (
442
432
 
443
433
  css += await parseRequirements(cssFiles, options);
444
434
 
435
+ console.log('[CSS BEFORE]', css);
436
+
437
+
445
438
  for (const style of styles) {
446
439
  css += style.content;
447
440
  }
448
441
 
442
+ css = css.replace(/[^{}]+:is\([^)]*\[.*?['"].*?\)[^{}]*\{[^}]*\}/g, '');
443
+
449
444
  const scopedCssFrontend = scopeCss(css, `.wp-block-${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(prefix))}-${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(name))}`);
450
445
  const editorStyleFile = scopeCss(css, `[data-type="${blockName}"]`);
451
446
  const scriptFile = js;
452
- let blockCode = await getBlock(htmlContent, options);
453
447
 
454
- blockCode = blockCode
455
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
456
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
457
- .replace(/<link[^>]*?>/gi, '');
448
+ htmlContent = htmlContent
449
+ .replace(/<head[\s\S]*?<\/head>/gi, '')
450
+ .replace(/<script[\s\S]*?<\/script>/gi, '')
451
+ .replace(/<style[\s\S]*?<\/style>/gi, '');
452
+
453
+
454
+ let blockCode = await getBlock(options);
455
+
456
+ blockCode = blockCode.replaceAll(' / dangerouslySetInnerHTML', ' dangerouslySetInnerHTML')
458
457
 
459
458
  const indexFile = getPhp(options);
459
+ let blockFile = '';
460
460
 
461
- const blockFile = transformBlockFile(blockCode).code
462
- .replace(/name: \"\{field.name\}\"/g, 'name: field.name')
463
- .replace(/key: \"\{index\}\"/g, 'key: index')
461
+ try {
462
+ blockFile = transformBlockFile(blockCode).code
463
+ ?.replace(/name: \"\{field.name\}\"/g, 'name: field.name')
464
+ ?.replace(/key: \"\{index\}\"/g, 'key: index')
465
+ } catch (error) {
466
+
467
+ console.log(error);
468
+
469
+ }
464
470
 
471
+ console.log(blockFile);
472
+
473
+
465
474
  if (shouldSaveFiles) {
466
- saveFile('style.css', scopedCssFrontend, options);
467
- saveFile('editor.css', editorStyleFile, options);
468
- saveFile('scripts.js', `${scriptFile}\n\n${emailTemplate}`, options);
469
- saveFile('index.php', indexFile, options);
470
- saveFile('block.js', blockFile, options);
471
- saveFile('remote-loader.js', '', options);
475
+ try {
476
+ saveFile('style.css', scopedCssFrontend, options);
477
+ saveFile('editor.css', editorStyleFile, options);
478
+ saveFile('scripts.js', `${scriptFile}\n\n${emailTemplate}`, options);
479
+ saveFile('index.php', indexFile, options);
480
+ saveFile('block.js', blockFile, options);
481
+ saveFile('remote-loader.js', '', options);
482
+ } catch (error) {
483
+ console.log(error);
484
+
485
+ }
472
486
  }
473
487
 
474
488
  return {
@@ -497,22 +511,14 @@ const block = async (
497
511
  return `${prefix}${suffix}`;
498
512
  };
499
513
 
500
- const functionSufix = generateRandomVariableName('func', 6);
514
+ const functionSufix = generateRandomVariableName('func');
501
515
 
502
516
  const setRandomAttributeContent = (randomVariableName, content) => {
503
- let newContent = content;
504
517
  const isArray = Array.isArray(content);
518
+
505
519
 
506
- if (typeof content === 'string') {
507
- newContent = content.replace(/(?:<[^>]*>|[^<"]*")|"/g, (match) => {
508
- if (match === '"') {
509
- return '&quot;';
510
- }
511
- return match;
512
- })
513
- }
514
-
515
- attributes[randomVariableName] = { type: isArray ? 'array' : 'string', default: newContent };
520
+ attributes[randomVariableName] = { type: isArray ? 'array' : 'string', default: content };
521
+
516
522
  };
517
523
 
518
524
  const hasAbsoluteKeyword = (str) => {
@@ -641,11 +647,29 @@ const block = async (
641
647
  const newPrefix = prefix ? replaceUnderscoresSpacesAndUppercaseLetters(prefix) : 'wp';
642
648
  const randomVariable = generateRandomVariableName(`${type}${newPrefix}`);
643
649
 
650
+ let imgSrcWithoutOrigin = imgSrc;
651
+ try {
652
+ if (typeof imgSrc === 'string') {
653
+ if (/^https?:\/\//.test(imgSrc)) {
654
+ const urlObj = new URL(imgSrc);
655
+ imgSrcWithoutOrigin = urlObj.pathname + urlObj.search + urlObj.hash;
656
+ } else if (source && imgSrc.startsWith(source)) {
657
+ imgSrcWithoutOrigin = imgSrc.slice(source.length);
658
+ if (imgSrcWithoutOrigin.startsWith('/')) imgSrcWithoutOrigin = imgSrcWithoutOrigin.slice(1);
659
+ }
660
+ }
661
+ } catch (e) {
662
+ imgSrcWithoutOrigin = imgSrc;
663
+ }
664
+ let imgSrcNoLeadingSlash = imgSrcWithoutOrigin;
665
+ if (typeof imgSrcNoLeadingSlash === 'string' && imgSrcNoLeadingSlash.startsWith('/')) {
666
+ imgSrcNoLeadingSlash = imgSrcNoLeadingSlash.slice(1);
667
+ }
644
668
  attributes[randomVariable] = {
645
669
  attribute,
646
670
  type: 'string',
647
671
  selector: 'img',
648
- default: attribute === 'alt' ? imgAlt : `var.url+'${imgSrc}'`,
672
+ default: attribute === 'alt' ? imgAlt : `{vars.url}${imgSrcNoLeadingSlash}`.replace(/^\u007f/, '$'),
649
673
  };
650
674
 
651
675
  imgTag.attr(attribute, `{attributes.${randomVariable}}`);
@@ -774,31 +798,25 @@ const block = async (
774
798
  };
775
799
 
776
800
  const getFixedHtml = (html) => {
777
- const onChangeRegex = / onChange="{" \(newtext\)=""\>/gi;
778
- const closeRichTextRegex = /<\/RichText>/gi;
779
- const valueBindingRegex = /value="{(.*?)}"/gi;
780
- const attributeBindingRegex = /"{attributes\.(.*?)}"/gi;
781
-
782
- const fixOnChange = ' onChange={ (newtext) => ';
783
- const fixValueBinding = 'value={$1}';
784
- const fixAttributeBinding = '{attributes.$1}';
785
-
786
801
  return html
787
- .replace(onChangeRegex, fixOnChange)
788
- .replace(closeRichTextRegex, '')
789
- .replace(valueBindingRegex, fixValueBinding)
790
- .replace(attributeBindingRegex, fixAttributeBinding);
802
+ .replace(/ onChange="{" \(newtext\)=""\>/gi, ' onChange={ (newtext) => ')
803
+ .replace(/\<\/RichText\>/gi, '')
804
+ .replace(/value="{(.*?)}"/gi, 'value={$1}')
805
+ .replace(/"{attributes.(.*?)}"/gi, '{attributes.$1}');
791
806
  };
792
-
793
807
 
794
808
  const processImages = (imgTag) => {
795
809
  const properties = getImageProperties(imgTag);
796
- const type = properties.isBackground ? 'background' : 'image';
797
-
798
- processImage({ ...properties, type });
799
- };
810
+ const { isBackground } = properties;
800
811
 
801
-
812
+ if (!isBackground) {
813
+ processImage({ ...properties, type: 'image' });
814
+
815
+ return;
816
+ }
817
+
818
+ processImage({ ...properties, type: 'background' });
819
+ };
802
820
  const loopImages = ($) => {
803
821
  $('img').each((_index, img) => {
804
822
  processImages($(img));
@@ -834,138 +852,85 @@ const block = async (
834
852
  return getRichTextTemplate(randomVariable, variableContent);
835
853
  };
836
854
 
837
- const containsCodeSyntax = (text) => {
838
- const codePattern = /{|}|\(|\)|=>/;
839
- return codePattern.test(text.trim());
840
- };
841
-
842
- const isEmptyText = (text) => {
843
- return text.trim() === '';
844
- };
845
-
846
855
  const parseContent = (content) => {
847
- return content.replace(/>([^<]+)</g, (match, text) => {
848
- const hasCodeSyntax = containsCodeSyntax(text);
849
- const isEmpty = isEmptyText(text);
856
+ return content.replace(/>([^<]+)</g, (match, variableContent) => {
857
+ const regex = /{|}|\(|\)|=>/;
858
+
859
+ if (regex.test(variableContent.trim())) {
860
+ return match;
861
+ }
850
862
 
851
- if (hasCodeSyntax || isEmpty) {
863
+ if (variableContent.trim() === '') {
852
864
  return match;
853
865
  }
854
866
 
855
- return convertToRichText(text);
867
+ return convertToRichText(variableContent);
856
868
  });
857
869
  };
858
-
859
- const removeHtmlComments = (html) => {
860
- return html.replaceAll(/<!--(.*?)-->/gs, '');
861
- };
862
-
863
- const wrapWithContainer = (html) => {
864
- return `<div className="custom-block">${html}</div>`;
865
- };
866
-
867
- const getEditJsxContent = async (options) => {
868
- const dynamicContent = transformFormToDynamicJSX(htmlContent);
869
- const withoutComments = removeHtmlComments(dynamicContent);
870
- const wrappedContent = wrapWithContainer(withoutComments);
871
-
872
- const parsedContent = convert(parseContent(wrappedContent));
873
870
 
871
+ const getEditJsxContent = async (options) => {
872
+ let content = transformFormToDynamicJSX(options.htmlContent);
873
+
874
+ content = content.replaceAll(/<!--(.*?)-->/gs, '');
875
+
876
+ content = `<div className="custom-block">${content}</div>`;
877
+
874
878
  return await processEditImages({
875
879
  ...options,
876
- htmlContent: parsedContent,
880
+ htmlContent: parseContent(content),
877
881
  });
878
882
  };
879
-
880
- const hasAttributes = (panel) => {
881
- return Array.isArray(panel.attributes) && panel.attributes.length > 0;
882
- };
883
883
 
884
- const createPanel = (panel) => {
885
- if (!hasAttributes(panel)) return;
886
-
887
- panels.push(panel);
888
- };
889
-
890
- const getSvgTemplate = (_match, attributesString, closingTag, variableName) => {
891
- return `
892
- <svg
893
- ${attributesString}
894
- dangerouslySetInnerHTML={{ __html: attributes.${variableName} }}
895
- >
896
- ${closingTag}
897
- `;
884
+ const createPanel = (values) => {
885
+ if (values.attributes && values.attributes.length > 0) {
886
+ panels.push(values);
887
+ }
898
888
  };
899
889
 
900
- const findSVGMatches = (html) => {
901
- const svgRegex = /<\s*svg\b((?:[^>'"]|"[^"]*"|'[^']*')*)>(\s*(?:[^<]|<(?!\/svg\s*>))*)(<\/\s*svg\s*>)/gim;
902
- return [...html.matchAll(svgRegex)];
903
- };
904
-
905
- const parseSVGMatch = (match) => {
906
- const [fullSvg, attributes, content, closingTag] = match;
907
- const start = match.index;
908
- const end = start + fullSvg.length;
909
-
910
- return { fullSvg, attributes, content, closingTag, start, end };
890
+ const getSvgTemplate = (_match, group1, _group3, randomSVGVariable) => {
891
+ return `<svg ${group1} dangerouslySetInnerHTML={ { __html: attributes.${randomSVGVariable} }}></svg>`;
911
892
  };
912
-
913
- const hasContent = (content) => content.trim() !== '';
914
-
915
- const handleSVGContent = (content) => {
916
- const variableName = generateRandomVariableName('svg');
917
- const cleanedContent = content.replaceAll('className', 'class');
918
-
919
- setRandomAttributeContent(variableName, cleanedContent);
920
-
921
- createPanel({
922
- type: 'svg',
923
- title: 'SVG Markup',
924
- attributes: [variableName],
925
- });
926
-
927
- return variableName;
928
- };
929
-
930
- const transformSVG = async (fullSvg, attributes, closingTag, variableName) => {
931
- const template = getSvgTemplate(fullSvg, attributes, closingTag, variableName);
932
- return await transform(template, { jsxRuntime: 'classic' });
933
- };
934
-
935
893
  const replaceSVGImages = async (html) => {
936
- const matches = findSVGMatches(html);
894
+ const regex = /<\s*svg\b((?:[^>'"]|"[^"]*"|'[^']*')*)>(\s*(?:[^<]|<(?!\/svg\s*>))*)(<\/\s*svg\s*>)/gim;
937
895
 
938
- let output = '';
939
- let cursor = 0;
896
+ let result = '';
897
+ let lastIndex = 0;
898
+ const matches = [...html.matchAll(regex)];
940
899
 
941
900
  for (const match of matches) {
942
- const { fullSvg, attributes, content, closingTag, start, end } = parseSVGMatch(match);
943
-
944
- output += html.slice(cursor, start);
945
-
946
- if (hasContent(content)) {
947
- const variableName = handleSVGContent(content);
948
-
949
- const transformed = await transformSVG(
950
- fullSvg,
951
- attributes,
952
- closingTag,
953
- variableName
954
- );
901
+ const [fullMatch, group1, group2, group3] = match;
902
+ const start = match.index;
903
+ const end = start + fullMatch.length;
904
+
905
+ result += html.slice(lastIndex, start);
906
+
907
+ const content = group2.trim();
908
+ if (content) {
909
+ const randomSVGVariable = generateRandomVariableName('svg');
910
+ setRandomAttributeContent(randomSVGVariable, content.replaceAll('className', 'class'));
911
+ createPanel({
912
+ type: 'svg',
913
+ title: 'SVG Markup',
914
+ attributes: [randomSVGVariable],
915
+ });
916
+
955
917
 
956
- output += transformed;
918
+ const replacement = getSvgTemplate(fullMatch, group1, group3, randomSVGVariable)
919
+
920
+ result += replacement;
957
921
  } else {
958
- output += fullSvg;
922
+ result += fullMatch;
959
923
  }
960
924
 
961
- cursor = end;
925
+ lastIndex = end;
962
926
  }
963
927
 
964
- output += html.slice(cursor);
965
-
966
- return output;
928
+ result += html.slice(lastIndex);
929
+
930
+ console.log(result);
931
+
932
+ return result;
967
933
  };
968
-
969
934
  const getSvgPanelTemplate = (panel) => {
970
935
  return panel.attributes && attributes[panel.attributes]
971
936
  ? `
@@ -1004,10 +969,7 @@ const block = async (
1004
969
  <PanelRow>
1005
970
  <div>
1006
971
  <MediaUpload
1007
- onSelect={ (media) => {
1008
- setAttributes({
1009
- ${mediaAtts}
1010
- });
972
+ onSelect={ (media) => { setAttributes({ ${mediaAtts} });
1011
973
  } }
1012
974
  type="image"
1013
975
  value={ attributes.${panel.attributes?.[0]} }
@@ -1316,7 +1278,7 @@ const block = async (
1316
1278
  /<RichText((.|\n)*?)value=\{(.*?)\}((.|\n)*?)\/>/gi,
1317
1279
  '<RichText.Content value={$3} />'
1318
1280
  )
1319
- .replace(/className=/gi, 'class=')
1281
+ .replaceAll('class=', 'className=')
1320
1282
  .replace(
1321
1283
  /<MediaUpload\b[^>]*>([\s\S]*?(<img\b[^>]*>*\/>)[\s\S]*?)\/>/g,
1322
1284
  (_match, _attributes, img) => {
@@ -1353,108 +1315,71 @@ const block = async (
1353
1315
  };
1354
1316
  };
1355
1317
 
1356
- const mergeAttributes = (attrString) => {
1357
- const attrRegex = /(\S+)=["'](.*?)["']/g;
1358
- const attrs = {};
1359
- let match;
1360
-
1361
- while ((match = attrRegex.exec(attrString)) !== null) {
1362
- attrs[match[1]] = match[2];
1363
- }
1364
- return attrs;
1365
- };
1366
-
1367
1318
  const unwrapAnchor = (htmlContent) => {
1368
-
1369
1319
  return htmlContent.replace(
1370
1320
  /<span([^>]*)>\s*<a([^>]*)>(.*?)<\/a>\s*<\/span>/gi,
1371
1321
  (_, spanAttrs, anchorAttrs, content) => {
1372
- const spanAttributes = mergeAttributes(spanAttrs);
1373
- const anchorAttributes = mergeAttributes(anchorAttrs);
1374
- const allAttributes = { ...spanAttributes, ...anchorAttributes };
1375
-
1376
- const attrString = Object.entries(allAttributes)
1322
+ const allAttrs = {};
1323
+ const attrRegex = /(\S+)=["'](.*?)["']/g;
1324
+
1325
+ let match;
1326
+ while ((match = attrRegex.exec(spanAttrs)) !== null) {
1327
+ allAttrs[match[1]] = match[2];
1328
+ }
1329
+
1330
+ while ((match = attrRegex.exec(anchorAttrs)) !== null) {
1331
+ allAttrs[match[1]] = match[2];
1332
+ }
1333
+
1334
+ return `<a ${Object.entries(allAttrs)
1377
1335
  .map(([key, value]) => `${key}="${value}"`)
1378
- .join(' ');
1379
-
1380
- return `<a ${attrString}>${content}</a>`;
1336
+ .join(' ')}>${content}</a>`;
1381
1337
  }
1382
1338
  );
1383
1339
  };
1384
-
1385
1340
 
1386
1341
  const getComponentAttributes = () => {
1387
- return JSON.stringify(attributes, null, 2);
1342
+ return Object.entries(attributes)
1343
+ .map(([key, value]) => {
1344
+ if (typeof value === 'object' && value !== null) {
1345
+ return `${key}: { ${Object.entries(value).map(([k, v]) => `${k}: \`${v}\``).join(', ')} }`;
1346
+ } else {
1347
+ return `${key}:\`${value}\``;
1348
+ }
1349
+ })
1350
+ .join(',\n');
1388
1351
  };
1389
1352
 
1390
1353
  const getEdit = async (options) => {
1391
1354
  let { htmlContent } = options;
1392
1355
 
1393
- if (!htmlContent) {
1394
- return '';
1356
+ if (htmlContent) {
1357
+ options.htmlContent = unwrapBody(htmlContent);
1358
+ const postProcessLinks = processLinks(options);
1359
+ const postGetEditJsx = await getEditJsxContent(postProcessLinks);
1360
+ const preConvert = await postGetEditJsx.replace(/<\/br>/g, '<br/>').replace(/<\/hr>/g, '<hr/>')
1361
+ return convert(preConvert)
1395
1362
  }
1396
1363
 
1397
- const processedOptions = processLinks(options);
1398
- const jsxContent = await getEditJsxContent(processedOptions);
1399
-
1400
- const replacedContent = jsxContent
1401
- .replace(/<article\b([^>]*)>/gi, '<div$1>')
1402
- .replace(/<\/article>/gi, '</div>')
1403
- .replaceAll('../', '')
1404
- .replace(/style="([^"]+)"/g, (_, styleString) => {
1405
- const styleObj = parseInlineStyle(styleString);
1406
- return `style={${styleObj}}`;
1407
- });
1408
-
1409
- return await replaceSVGImages(replacedContent);
1364
+ return '';
1410
1365
  };
1411
-
1412
1366
 
1413
1367
  const parseBlockAttributes = () => {
1414
- const attributes = getComponentAttributes();
1415
- const jsonString = JSON.stringify(attributes, null, 2);
1416
-
1417
- return jsonString
1418
- .replace(/"var.url\+\'(.*?)\'(.*?)"/g, "vars.url+'$1'$2")
1419
- .replace(/var(.*?).url\+'(http.*?)'/g, 'http$2');
1420
- };
1421
-
1422
- const fixDangerouslySetInnerHTML = (edit) => {
1423
- const pattern = /dangerouslySetInnerHTML="{" {="" __html:="" (.*?)="" }}=""/gm;
1424
- const replacement = 'dangerouslySetInnerHTML={{ __html: $1 }}';
1368
+ const attrs = `{${getComponentAttributes()}}`;
1369
+ return replaceSourceUrlVars(attrs, options.source);
1370
+ }
1425
1371
 
1426
- return edit.replace(pattern, replacement);
1427
- };
1428
-
1429
- const getBlock = async (htmlContent, settings) => {
1372
+ const getBlock = async (settings) => {
1430
1373
  let {
1431
1374
  name,
1432
1375
  category,
1433
1376
  generateIconPreview,
1434
- basePath,
1435
- cssFiles = [],
1436
- jsFiles = [],
1437
1377
  } = settings;
1438
1378
 
1439
- let iconPreview = "'shield'";
1440
- let edit = await getEdit(settings);
1441
-
1442
- edit = fixDangerouslySetInnerHTML(edit);
1443
-
1379
+ const iconPreview = generateIconPreview ? `(<img src={vars.url + 'preview.jpeg'} />)` : "'shield'";
1380
+ const edit = await getEdit(settings);
1444
1381
  const save = buildSaveContent(edit);
1445
- const blockPanels = createPanels();
1446
- const blockAttributes = parseBlockAttributes();
1447
-
1448
- if (generateIconPreview) {
1449
- try {
1450
- await icon(htmlContent, { basePath, cssFiles, jsFiles });
1451
- iconPreview = `(<img src="data:image/jpeg;base64,${await imageToBase64(
1452
- path.join(basePath, 'preview.jpeg')
1453
- )}" />)`;
1454
- } catch (error) {
1455
- console.log(`There was an error generating preview. ${error.message}`);
1456
- }
1457
- }
1382
+ const blockPanels = createPanels();
1458
1383
 
1459
1384
  const output = `
1460
1385
  (function () {
@@ -1464,7 +1389,7 @@ const block = async (
1464
1389
  title: '${name}',
1465
1390
  icon: ${iconPreview},
1466
1391
  category: '${category}',
1467
- attributes: ${blockAttributes},
1392
+ attributes: ${parseBlockAttributes()},
1468
1393
  edit(props) {
1469
1394
  const { attributes, setAttributes } = props;
1470
1395
 
@@ -1488,16 +1413,13 @@ const block = async (
1488
1413
  });
1489
1414
  })();`;
1490
1415
 
1491
- if (generateIconPreview) {
1492
- return output.replace(/icon: \s * (')([^']*)(')/, 'icon: $2');
1493
- }
1494
-
1495
1416
  return output;
1496
1417
  };
1497
1418
 
1498
1419
  const setupVariables = async (htmlContent, options) => {
1499
- const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
1500
- const linkRegex = /<link\s+[^>]*href=["']([^"']+)["'][^>]*>/gi;
1420
+
1421
+ const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
1422
+ const linkRegex = /<link\b[^>]*((\brel=["']stylesheet["'])|\bhref=["'][^"']+\.css["'])[^>]*>/gi;
1501
1423
 
1502
1424
  let match;
1503
1425
 
@@ -1523,7 +1445,11 @@ const block = async (
1523
1445
  css += styles.map((style) => {
1524
1446
  return `${style.content}`;
1525
1447
  }).join('\n');
1526
-
1448
+
1449
+
1450
+ console.log('[CSSFETCHED]', css);
1451
+
1452
+
1527
1453
 
1528
1454
  const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
1529
1455
  const scriptSrcRegex =
@@ -1537,9 +1463,7 @@ const block = async (
1537
1463
  }
1538
1464
  return '';
1539
1465
  });
1540
- const $ = cheerio.load(htmlContent, { xmlMode: true, decodeEntities: false });
1541
- $('head').remove();
1542
- htmlContent = $.html({ xml: false, decodeEntities: false });
1466
+
1543
1467
  const fetchJsPromises = [];
1544
1468
 
1545
1469
  while ((jsMatch = scriptSrcRegex.exec(htmlContent)) !== null) {
@@ -1566,6 +1490,18 @@ const block = async (
1566
1490
 
1567
1491
  const newDir = path.join(basePath, replaceUnderscoresSpacesAndUppercaseLetters(name));
1568
1492
 
1493
+ const $ = cheerio.load(htmlContent, {
1494
+ xmlMode: true,
1495
+ decodeEntities: false,
1496
+ });
1497
+
1498
+ $('head, script, style').remove();
1499
+
1500
+ htmlContent = $('body').html();
1501
+
1502
+ options.html
1503
+
1504
+
1569
1505
  try {
1570
1506
  fs.mkdirSync(newDir, { recursive: true });
1571
1507
 
@@ -1585,7 +1521,14 @@ const block = async (
1585
1521
  htmlContent = replaceRelativeUrlsInHtml(htmlContent, source);
1586
1522
  htmlContent = replaceRelativeUrlsInCssWithBase(htmlContent, source);
1587
1523
  }
1524
+
1525
+ try {
1526
+ icon(htmlContent, { basePath: path.join(options.basePath, replaceUnderscoresSpacesAndUppercaseLetters(options.name)) });
1527
+ } catch (error) {
1528
+ console.log(`There was an error generating preview. ${error.message}`);
1529
+ }
1588
1530
 
1589
1531
  return saveFiles(await setupVariables(htmlContent, options));
1590
1532
  };
1533
+
1591
1534
  export default block;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "@babel/preset-react": "^7.28.5",
5
5
  "@svgr/core": "^8.1.0",
6
6
  "cheerio": "^1.1.2",
7
- "css-scoping": "^1.0.0",
7
+ "css-scoping": "^1.0.1",
8
8
  "fetch-page-assets": "^1.2.5",
9
9
  "fs": "^0.0.1-security",
10
10
  "html-screenshots": "^1.2.7",
@@ -23,7 +23,7 @@
23
23
  "type": "module",
24
24
  "types": "index.ts",
25
25
  "name": "html-to-gutenberg",
26
- "version": "4.2.4",
26
+ "version": "4.2.5",
27
27
  "description": "Transform any valid HTML string into fully editable WP Gutenberg blocks in seconds rather than hours.",
28
28
  "main": "index.js",
29
29
  "directories": {
@@ -56,4 +56,4 @@
56
56
  "url": "https://github.com/DiogoAngelim/html-to-gutenberg/issues"
57
57
  },
58
58
  "homepage": "https://www.html-to-gutenberg.io"
59
- }
59
+ }