html-to-gutenberg 4.2.8 → 4.2.10
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/.env.example +20 -0
- package/.eslintrc.json +35 -0
- package/.github/workflows/build.yml +26 -0
- package/.github/workflows/coverage.yml +26 -0
- package/.github/workflows/sync-npm.yml +154 -0
- package/.nyc_output/1f0406b8-bb70-495d-8f8a-521fdd81b500.json +1 -0
- package/.nyc_output/6390956f-4f8a-4adb-9256-4a1c7e34a52d.json +1 -0
- package/.nyc_output/processinfo/1f0406b8-bb70-495d-8f8a-521fdd81b500.json +1 -0
- package/.nyc_output/processinfo/6390956f-4f8a-4adb-9256-4a1c7e34a52d.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/@types.d.ts +3 -0
- package/coverage/coverage-final.json +4 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +198 -0
- package/coverage-demo.test.ts +8 -0
- package/dist/coverage-demo.test.js +10 -0
- package/dist/coverage-demo.test.js.map +1 -0
- package/dist/globals.js +24 -0
- package/dist/globals.js.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.js +166 -0
- package/dist/index.test.js.map +1 -0
- package/dist/package.json +130 -0
- package/dist/snapapi-screenshot.test.js +44 -0
- package/dist/snapapi-screenshot.test.js.map +1 -0
- package/dist/src/coverage-demo.js +7 -0
- package/dist/src/coverage-demo.js.map +1 -0
- package/dist/src/utils-extra.test.js +137 -0
- package/dist/src/utils-extra.test.js.map +1 -0
- package/dist/src/utils.test.js +65 -0
- package/dist/src/utils.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils.js +61 -0
- package/dist/utils.js.map +1 -0
- package/fetch-page-assets.test.ts +448 -0
- package/index.d.ts +173 -0
- package/index.js +628 -249
- package/index.test.ts +774 -0
- package/index.ts +155 -1530
- package/package.json +87 -15
- package/r2.js +163 -0
- package/readme.md +126 -72
- package/scripts/patch-fetch-page-assets.mjs +13 -0
- package/scripts/sync-from-npm.mjs +115 -0
- package/snapapi-screenshot.test.ts +46 -0
- package/src/coverage-demo.ts +3 -0
- package/src/utils-extra.test.ts +108 -0
- package/src/utils.test.ts +36 -0
- package/temp-block-test.js +19 -0
- package/tsconfig.json +25 -4
- package/utils.ts +56 -0
- package/vendor/fetch-page-assets/LICENSE.MD +21 -0
- package/vendor/fetch-page-assets/README.md +117 -0
- package/vendor/fetch-page-assets/index.js +362 -0
- package/vendor/fetch-page-assets/package.json +48 -0
package/index.ts
CHANGED
|
@@ -1,1548 +1,173 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
imports,
|
|
14
|
-
images,
|
|
15
|
-
characters
|
|
16
|
-
} from './globals.js';
|
|
17
|
-
|
|
18
|
-
const require = createRequire(import.meta.url);
|
|
19
|
-
const { version } = require('./package.json');
|
|
20
|
-
|
|
21
|
-
const block = async (
|
|
22
|
-
htmlContent,
|
|
23
|
-
options = {
|
|
24
|
-
name: 'My block',
|
|
25
|
-
prefix: 'wp',
|
|
26
|
-
category: 'common',
|
|
27
|
-
basePath: process.cwd(),
|
|
28
|
-
shouldSaveFiles: true,
|
|
29
|
-
generateIconPreview: false,
|
|
30
|
-
jsFiles: [],
|
|
31
|
-
cssFiles: [],
|
|
32
|
-
source: null,
|
|
33
|
-
}
|
|
34
|
-
) => {
|
|
35
|
-
const panels = [];
|
|
36
|
-
const styles = [];
|
|
37
|
-
const scripts = [];
|
|
38
|
-
const attributes = {};
|
|
39
|
-
const formVars = {};
|
|
40
|
-
|
|
41
|
-
const { name, prefix, source } = options;
|
|
42
|
-
|
|
43
|
-
let js = '';
|
|
44
|
-
let css = '';
|
|
45
|
-
let phpEmailData = '';
|
|
46
|
-
let emailTemplate = '';
|
|
47
|
-
|
|
48
|
-
function hasTailwindCdnSource(jsFiles) {
|
|
49
|
-
const tailwindCdnRegex = /https:\/\/(cdn\.tailwindcss\.com(\?[^"'\s]*)?|cdn\.jsdelivr\.net\/npm\/@tailwindcss\/browser@4(\.\d+){0,2})/;
|
|
50
|
-
|
|
51
|
-
return jsFiles.some(url => tailwindCdnRegex.test(url));
|
|
52
|
-
}
|
|
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}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function sanitizeAndReplaceLeadingNumbers(str) {
|
|
63
|
-
const numberWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
|
|
64
|
-
let firstNumberReplaced = false;
|
|
65
|
-
|
|
66
|
-
return str
|
|
67
|
-
.toLowerCase()
|
|
68
|
-
.replace(/[\s\-_]/g, '')
|
|
69
|
-
.replace(/\d/g, (digit) => {
|
|
70
|
-
if (!firstNumberReplaced) {
|
|
71
|
-
firstNumberReplaced = true;
|
|
72
|
-
|
|
73
|
-
return numberWords[parseInt(digit)] + digit;
|
|
74
|
-
}
|
|
75
|
-
return digit;
|
|
76
|
-
})
|
|
77
|
-
.replace(/^[^a-z]+/, '');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const replaceUnderscoresSpacesAndUppercaseLetters = (name = '') => {
|
|
81
|
-
return name.replace(new RegExp(/\W|_/, 'g'), '-').toLowerCase();
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const saveFile = (fileName, contents, options) => {
|
|
85
|
-
try {
|
|
86
|
-
const filePath = path.join(options.basePath, fileName);
|
|
87
|
-
|
|
88
|
-
fs.writeFileSync(filePath, contents);
|
|
89
|
-
|
|
90
|
-
return contents;
|
|
91
|
-
} catch (error) {
|
|
92
|
-
logError(error);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
function replaceRelativeUrls(html, replacer) {
|
|
97
|
-
const urlAttributes = [
|
|
98
|
-
'src', 'href', 'action', 'srcset', 'poster', 'data', 'formaction'
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
const regex = new RegExp(
|
|
102
|
-
`\\b(${urlAttributes.join('|')}|data-[a-zA-Z0-9_-]+)\\s*=\\s*(['"])(?!https?:|//|mailto:|tel:|#)([^'"]+)\\2`,
|
|
103
|
-
'gi'
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
return html.replace(regex, (_match, attr, quote, url) => {
|
|
107
|
-
const newUrl = replacer(url);
|
|
108
|
-
return `${attr}=${quote}${newUrl}${quote}`;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
function replaceRelativeUrlsInCss(css, replacer) {
|
|
114
|
-
const regex = /url\(\s*(['"]?)(.+?)\1\s*\)/gi;
|
|
115
|
-
|
|
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
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function replaceRelativeUrlsInHtml(html, baseUrl) {
|
|
126
|
-
return replaceRelativeUrls(html, (url) => {
|
|
127
|
-
return new URL(url, baseUrl).href;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function replaceRelativeUrlsInCssWithBase(css, cssFileUrl) {
|
|
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
|
-
|
|
140
|
-
const parseRequirements = async (files, options) => {
|
|
141
|
-
const { source } = options;
|
|
142
|
-
let output = '';
|
|
143
|
-
|
|
144
|
-
for (const file of files) {
|
|
145
|
-
try {
|
|
146
|
-
const response = await fetch(file);
|
|
147
|
-
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
let data = await response.text();
|
|
153
|
-
|
|
154
|
-
if (source) {
|
|
155
|
-
data = replaceRelativeUrlsInCssWithBase(data, file);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
output += `${data}\n\n`;
|
|
159
|
-
} catch (error) {
|
|
160
|
-
logError(error);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return output;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const convertDashesSpacesAndUppercaseToUnderscoresAndLowercase = (string) => {
|
|
168
|
-
if (string) {
|
|
169
|
-
return `${string.replaceAll('-', '_').replaceAll(' ', '_').toLowerCase()}`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return '';
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const newName = replaceUnderscoresSpacesAndUppercaseLetters(name);
|
|
176
|
-
const blockName = `${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(prefix))}/${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(name))}`;
|
|
177
|
-
const blockNameHandle = `${prefix}-${newName}`;
|
|
178
|
-
|
|
179
|
-
const getPhp = (options) => {
|
|
180
|
-
const { name, prefix, jsFiles, cssFiles } = options;
|
|
181
|
-
const phpName = convertDashesSpacesAndUppercaseToUnderscoresAndLowercase(name);
|
|
182
|
-
const phpPrefix = `${functionSufix}${convertDashesSpacesAndUppercaseToUnderscoresAndLowercase(prefix)}`;
|
|
183
|
-
const tailwindFix = hasTailwindCdnSource(jsFiles) ? 'function forceTailwindUpdate(){let e=setInterval(()=>{if("undefined"!=typeof tailwind){clearInterval(e);let n=document.documentElement.outerHTML;tailwind.config.content=[{raw:n,extension:"html"}]}},100)}forceTailwindUpdate();' : '';
|
|
184
|
-
const tailwindFooter = hasTailwindCdnSource(jsFiles) ? '(()=>{if(window.__tailwindObserverActive)return;window.__tailwindObserverActive=!0;let e=new MutationObserver(()=>{let t=[...document.querySelectorAll("style")].find(e=>e.innerText.includes("--tw-"));t&&(document.body.appendChild(t),e.disconnect(),window.__tailwindObserverActive=!1)});e.observe(document.documentElement,{childList:!0,subtree:!0})})();' : '';
|
|
185
|
-
const inlineRemoteLoader = `var remoteUrls = ${JSON.stringify(jsFiles)};(function loadScripts() {window._loadedRemoteScripts = window._loadedRemoteScripts || new Set();const style = document.createElement('style');remoteUrls.forEach((url) => {if (window._loadedRemoteScripts.has(url)) return;const script = document.createElement('script');script.src = url;document.head.appendChild(script);window._loadedRemoteScripts.add(url);});})();${tailwindFix}${tailwindFooter}`;
|
|
186
|
-
|
|
187
|
-
const enqueueRemoteStyles = cssFiles
|
|
188
|
-
.map((remoteUrl) => {
|
|
189
|
-
return `
|
|
190
|
-
wp_enqueue_style(
|
|
191
|
-
'${blockNameHandle}-${remoteUrl
|
|
192
|
-
.split('/')
|
|
193
|
-
.pop()
|
|
194
|
-
.replace(/\.\w+$/, '')}',
|
|
195
|
-
'${remoteUrl}',
|
|
196
|
-
array(),
|
|
197
|
-
null
|
|
198
|
-
);
|
|
199
|
-
`;
|
|
200
|
-
})
|
|
201
|
-
.join('\n');
|
|
202
|
-
|
|
203
|
-
return `<?php
|
|
204
|
-
/*
|
|
205
|
-
* Plugin Name: ${name}
|
|
206
|
-
* Version: ${version}
|
|
207
|
-
* Author: Html to Gutenberg
|
|
208
|
-
* Author URI: https://www.html-to-gutenberg.io/
|
|
209
|
-
* Description: A custom editable block built with Html to Gutenberg
|
|
210
|
-
|
|
211
|
-
*/
|
|
212
|
-
|
|
213
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
214
|
-
exit;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function parse_form_placeholders_${functionSufix}($content, $post_data)
|
|
218
|
-
{
|
|
219
|
-
return preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($post_data) {
|
|
220
|
-
$key = trim($matches[1]);
|
|
221
|
-
return isset($post_data[$key]) ? sanitize_text_field($post_data[$key]) : '';
|
|
222
|
-
}, $content);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
add_action('wp_ajax_send_test_email_${functionSufix}}', function() {
|
|
226
|
-
//check_ajax_referer('wp_rest');
|
|
227
|
-
|
|
228
|
-
$post_data = $_POST;
|
|
229
|
-
$to = sanitize_email(parse_form_placeholders_${functionSufix}($post_data['to'], $post_data));
|
|
230
|
-
$from = sanitize_email(parse_form_placeholders_${functionSufix}($post_data['from'], $post_data));
|
|
231
|
-
$subject = sanitize_text_field(parse_form_placeholders_${functionSufix}($post_data['subject'], $post_data));
|
|
232
|
-
$message = wp_kses_post(parse_form_placeholders_${functionSufix}($post_data['message'], $post_data));
|
|
233
|
-
|
|
234
|
-
if (empty($to) || empty($from)) {
|
|
235
|
-
wp_send_json_error('Missing email addresses.');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
$sent = wp_mail($to, $subject, $message, [
|
|
239
|
-
'Content-Type: text/html; charset=UTF-8',
|
|
240
|
-
'From: ' . $from
|
|
241
|
-
]);
|
|
242
|
-
|
|
243
|
-
if ($sent) {
|
|
244
|
-
wp_send_json_success('Email sent');
|
|
245
|
-
} else {
|
|
246
|
-
error_log('Failed to send. To: ' . $to . ' | From: ' . $from);
|
|
247
|
-
wp_send_json_error('Failed to send email.');
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
${phpEmailData}
|
|
252
|
-
|
|
253
|
-
function ${phpPrefix}_${phpName}_add_custom_editor_styles() {
|
|
254
|
-
echo '<style>span.block-editor-rich-text__editable.rich-text{all:unset!important}a br[data-rich-text-line-break=true],span.block-editor-block-icon.block-editor-block-switcher__toggle.has-colors img{display:none}.block-editor-block-types-list__list-item{width:100%!important}.block-editor-block-list__layout.is-root-container>:where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width:100%;margin:0}[aria-label="Empty block; start writing or type forward slash to choose a block"]{max-width:1200px!important}span.block-editor-block-types-list__item-icon img{max-width:100%;width:100%;margin:0;display:block}span.block-editor-block-icon.has-colors{all:inherit;order:2;flex:0 0 100%;width:100%}span.block-editor-block-icon.has-colors svg{margin-left:auto;margin-right:auto}.block-editor-block-card{display:flex!important;flex-wrap:wrap}.block-editor-inserter__preview-content-missing{display:none!important}</style>';
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
add_action('admin_footer', function () {
|
|
258
|
-
$screen = get_current_screen();
|
|
259
|
-
|
|
260
|
-
if ($screen && method_exists($screen, 'is_block_editor') && $screen->is_block_editor()) {
|
|
261
|
-
$href = esc_url(plugins_url('editor.css', __FILE__));
|
|
262
|
-
echo "<link rel='stylesheet' id='${blockNameHandle}-style' href='$href' type='text/css' media='all' />";
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
add_action( 'enqueue_block_editor_assets', '${phpPrefix}_${phpName}_editor_assets' );
|
|
267
|
-
|
|
268
|
-
add_action('wp_enqueue_scripts', function() {
|
|
269
|
-
wp_dequeue_style('wp-fonts-local');
|
|
270
|
-
wp_deregister_style('wp-fonts-local');
|
|
271
|
-
wp_dequeue_style('global-styles');
|
|
272
|
-
wp_deregister_style('global-styles');
|
|
273
|
-
remove_action('wp_footer', 'wp_global_styles_render_svg_filters');
|
|
274
|
-
}, 100);
|
|
275
|
-
|
|
276
|
-
function ${phpPrefix}_${phpName}_editor_assets() {
|
|
277
|
-
$filepath = plugin_dir_path(__FILE__) . 'block.js';
|
|
278
|
-
$version = file_exists($filepath) ? filemtime($filepath) : time();
|
|
279
|
-
|
|
280
|
-
wp_enqueue_script(
|
|
281
|
-
'${blockNameHandle}',
|
|
282
|
-
plugins_url( 'block.js', __FILE__ ),
|
|
283
|
-
array( 'wp-blocks', 'wp-components', 'wp-element' ,'wp-editor'),
|
|
284
|
-
$version
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
wp_localize_script( '${blockNameHandle}', 'vars', array( 'url' => plugin_dir_url( __FILE__ ) ) );
|
|
288
|
-
|
|
289
|
-
${enqueueRemoteStyles}
|
|
290
|
-
|
|
291
|
-
${phpPrefix}_${phpName}_add_custom_editor_styles();
|
|
292
|
-
|
|
293
|
-
wp_dequeue_style('${blockNameHandle}-frontend');
|
|
294
|
-
wp_deregister_style('${blockNameHandle}-frontend');
|
|
295
|
-
|
|
296
|
-
wp_enqueue_script(
|
|
297
|
-
'${blockNameHandle}-remote-loader',
|
|
298
|
-
plugins_url('remote-loader.js', __FILE__),
|
|
299
|
-
array(),
|
|
300
|
-
null,
|
|
301
|
-
true
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
wp_add_inline_script(
|
|
305
|
-
'${blockNameHandle}-remote-loader',
|
|
306
|
-
${JSON.stringify(inlineRemoteLoader)}
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
add_action('enqueue_block_editor_assets', function () {
|
|
311
|
-
wp_dequeue_style('wp-block-library');
|
|
312
|
-
wp_dequeue_style('wp-block-library-theme');
|
|
313
|
-
wp_dequeue_style('wc-block-style');
|
|
314
|
-
wp_dequeue_style('wp-format-library');
|
|
315
|
-
}, 100);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
add_action('init', function () {
|
|
319
|
-
wp_register_script(
|
|
320
|
-
'${blockNameHandle}-scripts',
|
|
321
|
-
plugins_url('scripts.js', __FILE__),
|
|
322
|
-
array(),
|
|
323
|
-
null,
|
|
324
|
-
true
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
wp_register_style(
|
|
328
|
-
'${blockNameHandle}-frontend',
|
|
329
|
-
plugins_url('style.css', __FILE__)
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
wp_register_script(
|
|
333
|
-
'${blockNameHandle}-remote-loader',
|
|
334
|
-
plugins_url('remote-loader.js', __FILE__),
|
|
335
|
-
array(),
|
|
336
|
-
null,
|
|
337
|
-
true
|
|
338
|
-
);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
add_action( 'wp_enqueue_scripts', '${phpPrefix}_${phpName}_block_assets', 999 );
|
|
342
|
-
|
|
343
|
-
function ${phpPrefix}_${phpName}_block_assets() {
|
|
344
|
-
global $wp_query;
|
|
345
|
-
|
|
346
|
-
$used = false;
|
|
347
|
-
|
|
348
|
-
if (!empty($wp_query->posts)) {
|
|
349
|
-
foreach ($wp_query->posts as $post) {
|
|
350
|
-
$blocks = parse_blocks($post->post_content);
|
|
351
|
-
|
|
352
|
-
foreach ($blocks as $block) {
|
|
353
|
-
if ($block['blockName'] === '${blockName}') {
|
|
354
|
-
$used = true;
|
|
355
|
-
break 2;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if ($used) {
|
|
362
|
-
$handle = '${blockNameHandle}';
|
|
363
|
-
|
|
364
|
-
wp_enqueue_style($handle . '-frontend');
|
|
365
|
-
|
|
366
|
-
wp_enqueue_script($handle . '-scripts');
|
|
367
|
-
|
|
368
|
-
wp_localize_script(
|
|
369
|
-
$handle . '-scripts',
|
|
370
|
-
'vars',
|
|
371
|
-
array(
|
|
372
|
-
'postId' => get_queried_object_id(),
|
|
373
|
-
'ajaxUrl' => admin_url('admin-ajax.php')
|
|
374
|
-
)
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
wp_enqueue_script($handle . '-remote-loader');
|
|
378
|
-
|
|
379
|
-
wp_add_inline_script(
|
|
380
|
-
$handle . '-remote-loader',
|
|
381
|
-
${JSON.stringify(inlineRemoteLoader)}
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
`;
|
|
386
|
-
};
|
|
387
|
-
|
|
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, {
|
|
411
|
-
presets: [[presetReact, { pragma: 'wp.element.createElement' }]],
|
|
412
|
-
filename: 'block.js'
|
|
413
|
-
});
|
|
414
|
-
} catch (error) {
|
|
415
|
-
console.log(error);
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return test;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const saveFiles = async (options) => {
|
|
423
|
-
const { cssFiles = [], jsFiles = [], shouldSaveFiles, name, prefix } = options;
|
|
424
|
-
const tailwindRegex = /(class|className)\s*=\s*["'][^"']*\b(items-center|justify-center|gap-\d+|rounded(-[a-z]+)?|text-[a-z]+-\d{3}|bg-[a-z]+-\d{3}|w-(full|screen)|h-(full|screen)|max-w-[\w\[\]-]+|p-\d+|m-\d+)\b[^"']*["']/i;
|
|
425
|
-
const hasTailwind = tailwindRegex.test(htmlContent);
|
|
426
|
-
const hasTailwindCdn = hasTailwindCdnSource(jsFiles);
|
|
427
|
-
|
|
428
|
-
css = hasTailwind && hasTailwindCdn ? '' : `
|
|
429
|
-
*:not(.components-button) {
|
|
430
|
-
all: revert-layer;
|
|
431
|
-
}\n`;
|
|
432
|
-
|
|
433
|
-
css += await parseRequirements(cssFiles, options);
|
|
434
|
-
|
|
435
|
-
console.log('[CSS BEFORE]', css);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
for (const style of styles) {
|
|
439
|
-
css += style.content;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
css = css.replace(/[^{}]+:is\([^)]*\[.*?['"].*?\)[^{}]*\{[^}]*\}/g, '');
|
|
443
|
-
|
|
444
|
-
const scopedCssFrontend = scopeCss(css, `.wp-block-${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(prefix))}-${sanitizeAndReplaceLeadingNumbers(replaceUnderscoresSpacesAndUppercaseLetters(name))}`);
|
|
445
|
-
const editorStyleFile = scopeCss(css, `[data-type="${blockName}"]`);
|
|
446
|
-
const scriptFile = js;
|
|
447
|
-
|
|
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')
|
|
457
|
-
|
|
458
|
-
const indexFile = getPhp(options);
|
|
459
|
-
let blockFile = '';
|
|
460
|
-
|
|
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
|
-
}
|
|
470
|
-
|
|
471
|
-
console.log(blockFile);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
if (shouldSaveFiles) {
|
|
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
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return {
|
|
489
|
-
'style.css': scopedCssFrontend,
|
|
490
|
-
'editor.css': editorStyleFile,
|
|
491
|
-
'scripts.js': scriptFile,
|
|
492
|
-
'index.php': indexFile,
|
|
493
|
-
'block.js': blockFile,
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
const logError = (error) => {
|
|
499
|
-
console.error(`[Error] ${error.message}`);
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
const generateRandomVariableName = (prefix = 'content', length = 3) => {
|
|
503
|
-
let suffix = '';
|
|
504
|
-
|
|
505
|
-
for (let i = 0; i < length; i++) {
|
|
506
|
-
suffix += characters.charAt(
|
|
507
|
-
Math.floor(Math.random() * characters.length)
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
return `${prefix}${suffix}`;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
const functionSufix = generateRandomVariableName('func');
|
|
515
|
-
|
|
516
|
-
const setRandomAttributeContent = (randomVariableName, content) => {
|
|
517
|
-
const isArray = Array.isArray(content);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
attributes[randomVariableName] = { type: isArray ? 'array' : 'string', default: content };
|
|
521
|
-
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
const hasAbsoluteKeyword = (str) => {
|
|
525
|
-
return !!(str && str.toLowerCase().includes('absolute'));
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
const getImageTemplate = (image) => {
|
|
529
|
-
const { randomUrlVariable, randomAltVariable, imgClass } = image;
|
|
530
|
-
return `
|
|
531
|
-
<MediaUpload
|
|
532
|
-
onSelect={(media) => {
|
|
533
|
-
setAttributes({
|
|
534
|
-
${randomUrlVariable}: media.url,
|
|
535
|
-
${randomAltVariable}: media.alt
|
|
536
|
-
});
|
|
537
|
-
}}
|
|
538
|
-
type="image"
|
|
539
|
-
render={({ open }) => (
|
|
540
|
-
<div style={{ position: 'relative' }}>
|
|
541
|
-
<img
|
|
542
|
-
src={attributes.${randomUrlVariable}}
|
|
543
|
-
alt={attributes.${randomAltVariable}}
|
|
544
|
-
className="${imgClass}"
|
|
545
|
-
/>
|
|
546
|
-
<div
|
|
547
|
-
onClick={open}
|
|
548
|
-
style={{
|
|
549
|
-
position: 'absolute',
|
|
550
|
-
bottom: '0',
|
|
551
|
-
width: '100%',
|
|
552
|
-
height: '100%',
|
|
553
|
-
zIndex: 10,
|
|
554
|
-
cursor: 'pointer'
|
|
555
|
-
}}
|
|
556
|
-
></div>
|
|
557
|
-
</div>
|
|
558
|
-
)}
|
|
559
|
-
/>
|
|
560
|
-
`;
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
const replaceHtmlImage = (html, image) => {
|
|
564
|
-
const { randomUrlVariable } = image;
|
|
565
|
-
const regex = new RegExp(`<img\\s+[^>]*src=\\{[^}]*${randomUrlVariable}[^}]*\\}[^>]*>`, 'gi');
|
|
566
|
-
return html.replace(regex, getImageTemplate(image));
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
const replaceImageComponents = (html) => {
|
|
570
|
-
images.forEach((image) => {
|
|
571
|
-
html = replaceHtmlImage(html, image);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
return html;
|
|
575
|
-
};
|
|
576
|
-
const loadHtml = async (options) => {
|
|
577
|
-
const { basePath, htmlContent } = options;
|
|
578
|
-
if (htmlContent) {
|
|
579
|
-
const newHtml = await extractAssets(htmlContent, {
|
|
580
|
-
basePath,
|
|
581
|
-
saveFile: true,
|
|
582
|
-
verbose: false,
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
return cheerio.load(newHtml, {
|
|
586
|
-
xmlMode: true,
|
|
587
|
-
decodeEntities: false,
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
const getImageSource = (imgTag) => {
|
|
592
|
-
return imgTag.attr('src') || '';
|
|
593
|
-
};
|
|
594
|
-
const getImageAlt = (imgTag) => {
|
|
595
|
-
return imgTag.attr('alt') || '';
|
|
596
|
-
};
|
|
597
|
-
const getParentElement = (imgTag) => {
|
|
598
|
-
return imgTag.parent();
|
|
599
|
-
};
|
|
600
|
-
const getImageStyle = (imgTag) => {
|
|
601
|
-
return imgTag.attr('style') || '';
|
|
602
|
-
};
|
|
603
|
-
const getImageClass = (imgTag) => {
|
|
604
|
-
return imgTag.attr('class') || imgTag.attr('className') || '';
|
|
605
|
-
};
|
|
606
|
-
const getPreviousStyle = (parentElement) => {
|
|
607
|
-
return parentElement.attr('style') || '';
|
|
608
|
-
};
|
|
609
|
-
const getParentClass = (parentElement) => {
|
|
610
|
-
return parentElement.attr('class') || parentElement.attr('className') || '';
|
|
611
|
-
};
|
|
612
|
-
const isBackgroundImage = (
|
|
613
|
-
imgStyle,
|
|
614
|
-
imgClass,
|
|
615
|
-
previousStyle,
|
|
616
|
-
previousClass
|
|
617
|
-
) => {
|
|
618
|
-
return (
|
|
619
|
-
hasAbsoluteKeyword(imgStyle) ||
|
|
620
|
-
hasAbsoluteKeyword(imgClass) ||
|
|
621
|
-
hasAbsoluteKeyword(previousStyle) ||
|
|
622
|
-
hasAbsoluteKeyword(previousClass)
|
|
623
|
-
);
|
|
624
|
-
};
|
|
625
|
-
const getImageProperties = (imgTag) => {
|
|
626
|
-
const parentElement = getParentElement(imgTag);
|
|
627
|
-
const imgStyle = getImageStyle(imgTag);
|
|
628
|
-
const imgClass = getImageClass(imgTag);
|
|
629
|
-
const previousStyle = getPreviousStyle(parentElement);
|
|
630
|
-
const previousClass = getParentClass(parentElement);
|
|
631
|
-
const isBackground = isBackgroundImage(
|
|
632
|
-
imgStyle,
|
|
633
|
-
imgClass,
|
|
634
|
-
previousStyle,
|
|
635
|
-
previousClass
|
|
636
|
-
);
|
|
637
|
-
return {
|
|
638
|
-
imgTag,
|
|
639
|
-
imgClass,
|
|
640
|
-
isBackground,
|
|
641
|
-
imgSrc: getImageSource(imgTag),
|
|
642
|
-
imgAlt: getImageAlt(imgTag),
|
|
643
|
-
};
|
|
644
|
-
};
|
|
645
|
-
const setImageAttribute = (properties) => {
|
|
646
|
-
const { imgTag, imgSrc, imgAlt, attribute, type, prefix } = properties;
|
|
647
|
-
const newPrefix = prefix ? replaceUnderscoresSpacesAndUppercaseLetters(prefix) : 'wp';
|
|
648
|
-
const randomVariable = generateRandomVariableName(`${type}${newPrefix}`);
|
|
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
|
-
}
|
|
668
|
-
attributes[randomVariable] = {
|
|
669
|
-
attribute,
|
|
670
|
-
type: 'string',
|
|
671
|
-
selector: 'img',
|
|
672
|
-
default: attribute === 'alt' ? imgAlt : `{vars.url}${imgSrcNoLeadingSlash}`.replace(/^\u007f/, '$'),
|
|
673
|
-
};
|
|
674
|
-
|
|
675
|
-
imgTag.attr(attribute, `{attributes.${randomVariable}}`);
|
|
676
|
-
|
|
677
|
-
return randomVariable;
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
const processImage = (properties) => {
|
|
681
|
-
const { imgClass, type } = properties;
|
|
682
|
-
|
|
683
|
-
const randomUrlVariable = setImageAttribute({
|
|
684
|
-
...properties,
|
|
685
|
-
attribute: 'src',
|
|
686
|
-
prefix: 'Url',
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
const randomAltVariable = setImageAttribute({
|
|
690
|
-
...properties,
|
|
691
|
-
attribute: 'alt',
|
|
692
|
-
prefix: 'Alt',
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
if (type !== 'background') {
|
|
696
|
-
images.push({ randomUrlVariable, randomAltVariable, imgClass });
|
|
697
|
-
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
createPanel({
|
|
702
|
-
type: 'media',
|
|
703
|
-
title: 'Background Image',
|
|
704
|
-
attributes: [randomUrlVariable, randomAltVariable],
|
|
705
|
-
});
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
const createPanelsForForm = () => {
|
|
709
|
-
const randomFormIdVariable = generateRandomVariableName('form');
|
|
710
|
-
const randomHiddenFieldsAttr = generateRandomVariableName('hiddenFields');
|
|
711
|
-
const randomSendEmailVariable = generateRandomVariableName('send');
|
|
712
|
-
const randomEmailFromVariable = generateRandomVariableName('emailFrom');
|
|
713
|
-
const randomEmailToVariable = generateRandomVariableName('emailTo');
|
|
714
|
-
const randomEmailSubjectVariable = generateRandomVariableName('emailSubj');
|
|
715
|
-
const randomEmailMessageVariable = generateRandomVariableName('emailMsg');
|
|
716
|
-
const randomTestFeedbackAttr = generateRandomVariableName('emailTestMsg');
|
|
717
|
-
|
|
718
|
-
Object.assign(formVars, {
|
|
719
|
-
randomFormIdVariable,
|
|
720
|
-
randomSendEmailVariable,
|
|
721
|
-
randomEmailFromVariable,
|
|
722
|
-
randomEmailToVariable,
|
|
723
|
-
randomEmailSubjectVariable,
|
|
724
|
-
randomEmailMessageVariable,
|
|
725
|
-
randomTestFeedbackAttr,
|
|
726
|
-
randomHiddenFieldsAttr
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
setRandomAttributeContent(randomFormIdVariable, `form-${randomFormIdVariable}`);
|
|
730
|
-
setRandomAttributeContent(randomSendEmailVariable, false);
|
|
731
|
-
setRandomAttributeContent(randomEmailFromVariable, '');
|
|
732
|
-
setRandomAttributeContent(randomEmailToVariable, '');
|
|
733
|
-
setRandomAttributeContent(randomEmailSubjectVariable, 'New Form Submission');
|
|
734
|
-
setRandomAttributeContent(randomEmailMessageVariable, 'Form data:\n{{fields}}');
|
|
735
|
-
setRandomAttributeContent(randomTestFeedbackAttr, '');
|
|
736
|
-
setRandomAttributeContent(randomHiddenFieldsAttr, []);
|
|
737
|
-
|
|
738
|
-
createPanel({
|
|
739
|
-
type: 'formSettings',
|
|
740
|
-
title: 'Form Settings',
|
|
741
|
-
attributes: [randomFormIdVariable, randomSendEmailVariable],
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
createPanel({
|
|
745
|
-
type: 'emailSettings',
|
|
746
|
-
title: 'Email Settings',
|
|
747
|
-
attributes: [
|
|
748
|
-
randomSendEmailVariable,
|
|
749
|
-
randomEmailFromVariable,
|
|
750
|
-
randomEmailToVariable,
|
|
751
|
-
randomEmailSubjectVariable,
|
|
752
|
-
randomEmailMessageVariable,
|
|
753
|
-
randomTestFeedbackAttr,
|
|
754
|
-
randomFormIdVariable
|
|
755
|
-
],
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
createPanel({
|
|
759
|
-
type: 'hiddenFields',
|
|
760
|
-
title: 'Hidden Fields',
|
|
761
|
-
attributes: [randomHiddenFieldsAttr],
|
|
762
|
-
});
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
const getFormVariables = () => ({ ...formVars });
|
|
766
|
-
|
|
767
|
-
const transformFormToDynamicJSX = (htmlContent) => {
|
|
768
|
-
const regex = /<form([\s\S]*?)>([\s\S]*?)<\/form>/gi
|
|
769
|
-
const formExists = regex.test(htmlContent);
|
|
770
|
-
|
|
771
|
-
if (!formExists) {
|
|
772
|
-
return htmlContent;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return htmlContent.replace(
|
|
777
|
-
/<form([\s\S]*?)>([\s\S]*?)<\/form>/gi,
|
|
778
|
-
(_match, formAttributes, innerContent) => {
|
|
779
|
-
createPanelsForForm();
|
|
780
|
-
|
|
781
|
-
const {
|
|
782
|
-
randomFormIdVariable,
|
|
783
|
-
randomHiddenFieldsAttr
|
|
784
|
-
} = getFormVariables();
|
|
785
|
-
|
|
786
|
-
return `
|
|
787
|
-
<form
|
|
788
|
-
${formAttributes.trim()}
|
|
789
|
-
id={attributes.${randomFormIdVariable}}
|
|
790
|
-
>
|
|
791
|
-
${innerContent}
|
|
792
|
-
|
|
793
|
-
{ (attributes.${randomHiddenFieldsAttr} || []).map((field, index) => (<input key={index} name={field.name} value={field.value} type="hidden" /> )) }
|
|
794
|
-
</form>
|
|
795
|
-
`;
|
|
796
|
-
}
|
|
797
|
-
);
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
const getFixedHtml = (html) => {
|
|
801
|
-
function parseStyleString(style) {
|
|
802
|
-
const entries = style.split(';').filter(Boolean).map(rule => {
|
|
803
|
-
const [key, value] = rule.split(':');
|
|
804
|
-
if (!key || !value) return null;
|
|
805
|
-
const camelKey = key.trim().replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
806
|
-
return [camelKey, value.trim()];
|
|
807
|
-
}).filter(Boolean);
|
|
808
|
-
const styleObject = Object.fromEntries(entries);
|
|
809
|
-
return JSON.stringify(styleObject).replace(/"([^"\n]+)":/g, '$1:');
|
|
810
|
-
}
|
|
811
|
-
return html
|
|
812
|
-
.replace(/style="([^"]+)"/g, (_, styleString) => {
|
|
813
|
-
const styleObj = parseStyleString(styleString);
|
|
814
|
-
return `style={${styleObj}}`;
|
|
815
|
-
})
|
|
816
|
-
.replace(/ onChange="{" \(newtext\)=""\>/gi, ' onChange={ (newtext) => ')
|
|
817
|
-
.replace(/\<\/RichText\>/gi, '')
|
|
818
|
-
.replace(/value="{(.*?)}"/gi, 'value={$1}')
|
|
819
|
-
.replace(/"{attributes.(.*?)}"/gi, '{attributes.$1}');
|
|
820
|
-
};
|
|
821
|
-
|
|
822
|
-
const processImages = (imgTag) => {
|
|
823
|
-
const properties = getImageProperties(imgTag);
|
|
824
|
-
const { isBackground } = properties;
|
|
825
|
-
|
|
826
|
-
if (!isBackground) {
|
|
827
|
-
processImage({ ...properties, type: 'image' });
|
|
828
|
-
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
processImage({ ...properties, type: 'background' });
|
|
833
|
-
};
|
|
834
|
-
const loopImages = ($) => {
|
|
835
|
-
$('img').each((_index, img) => {
|
|
836
|
-
processImages($(img));
|
|
837
|
-
});
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
const getHtml = ($) => {
|
|
841
|
-
return $.html({ xml: false, decodeEntities: false });
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
const processEditImages = async (options) => {
|
|
845
|
-
const $ = await loadHtml(options);
|
|
846
|
-
loopImages($);
|
|
847
|
-
return replaceImageComponents(getFixedHtml(getHtml($)));
|
|
848
|
-
};
|
|
849
|
-
const getRichTextTemplate = (randomVariable, variableContent) => {
|
|
850
|
-
return `
|
|
851
|
-
><RichText
|
|
852
|
-
tagName="span"
|
|
853
|
-
value={attributes.${randomVariable}}
|
|
854
|
-
default="${variableContent.trim().replace(/"/g, '"')}"
|
|
855
|
-
onChange={ (newtext) => {
|
|
856
|
-
setAttributes({ ${randomVariable}: newtext });
|
|
857
|
-
}}
|
|
858
|
-
/><`;
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
const convertToRichText = (variableContent) => {
|
|
862
|
-
const randomVariable = generateRandomVariableName('content');
|
|
863
|
-
|
|
864
|
-
setRandomAttributeContent(randomVariable, variableContent);
|
|
865
|
-
|
|
866
|
-
return getRichTextTemplate(randomVariable, variableContent);
|
|
867
|
-
};
|
|
868
|
-
|
|
869
|
-
const parseContent = (content) => {
|
|
870
|
-
return content.replace(/>([^<]+)</g, (match, variableContent) => {
|
|
871
|
-
const regex = /{|}|\(|\)|=>/;
|
|
872
|
-
|
|
873
|
-
if (regex.test(variableContent.trim())) {
|
|
874
|
-
return match;
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
if (variableContent.trim() === '') {
|
|
878
|
-
return match;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
return convertToRichText(variableContent);
|
|
882
|
-
});
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
const getEditJsxContent = async (options) => {
|
|
886
|
-
let content = transformFormToDynamicJSX(options.htmlContent);
|
|
887
|
-
|
|
888
|
-
content = content.replaceAll(/<!--(.*?)-->/gs, '');
|
|
889
|
-
|
|
890
|
-
content = `<div className="custom-block">${content}</div>`;
|
|
891
|
-
|
|
892
|
-
return await processEditImages({
|
|
893
|
-
...options,
|
|
894
|
-
htmlContent: parseContent(content),
|
|
895
|
-
});
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
const createPanel = (values) => {
|
|
899
|
-
if (values.attributes && values.attributes.length > 0) {
|
|
900
|
-
panels.push(values);
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
|
|
904
|
-
const getSvgTemplate = (_match, group1, _group3, randomSVGVariable) => {
|
|
905
|
-
return `<svg ${group1} dangerouslySetInnerHTML={ { __html: attributes.${randomSVGVariable} }}></svg>`;
|
|
906
|
-
};
|
|
907
|
-
const replaceSVGImages = async (html) => {
|
|
908
|
-
const regex = /<\s*svg\b((?:[^>'"]|"[^"]*"|'[^']*')*)>(\s*(?:[^<]|<(?!\/svg\s*>))*)(<\/\s*svg\s*>)/gim;
|
|
909
|
-
|
|
910
|
-
let result = '';
|
|
911
|
-
let lastIndex = 0;
|
|
912
|
-
const matches = [...html.matchAll(regex)];
|
|
913
|
-
|
|
914
|
-
for (const match of matches) {
|
|
915
|
-
const [fullMatch, group1, group2, group3] = match;
|
|
916
|
-
const start = match.index;
|
|
917
|
-
const end = start + fullMatch.length;
|
|
918
|
-
|
|
919
|
-
result += html.slice(lastIndex, start);
|
|
920
|
-
|
|
921
|
-
const content = group2.trim();
|
|
922
|
-
if (content) {
|
|
923
|
-
const randomSVGVariable = generateRandomVariableName('svg');
|
|
924
|
-
setRandomAttributeContent(randomSVGVariable, content.replaceAll('className', 'class'));
|
|
925
|
-
createPanel({
|
|
926
|
-
type: 'svg',
|
|
927
|
-
title: 'SVG Markup',
|
|
928
|
-
attributes: [randomSVGVariable],
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const replacement = getSvgTemplate(fullMatch, group1, group3, randomSVGVariable)
|
|
933
|
-
|
|
934
|
-
result += replacement;
|
|
935
|
-
} else {
|
|
936
|
-
result += fullMatch;
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
lastIndex = end;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
result += html.slice(lastIndex);
|
|
943
|
-
|
|
944
|
-
console.log(result);
|
|
945
|
-
|
|
946
|
-
return result;
|
|
947
|
-
};
|
|
948
|
-
const getSvgPanelTemplate = (panel) => {
|
|
949
|
-
return panel.attributes && attributes[panel.attributes]
|
|
950
|
-
? `
|
|
951
|
-
{ (
|
|
952
|
-
<PanelBody title="${panel.title}">
|
|
953
|
-
<PanelRow>
|
|
954
|
-
<div>
|
|
955
|
-
<TextareaControl
|
|
956
|
-
label="SVG Content"
|
|
957
|
-
help="Enter your SVG content..."
|
|
958
|
-
value={ attributes.${panel.attributes} }
|
|
959
|
-
onChange={ ( value ) => {
|
|
960
|
-
setAttributes({ ${panel.attributes}: value });
|
|
961
|
-
} }
|
|
962
|
-
/>
|
|
963
|
-
</div>
|
|
964
|
-
</PanelRow>
|
|
965
|
-
</PanelBody>
|
|
966
|
-
)}
|
|
967
|
-
`
|
|
968
|
-
: '';
|
|
969
|
-
};
|
|
970
|
-
|
|
971
|
-
const getMediaPanelTemplate = (panel) => {
|
|
972
|
-
const mediaAtts =
|
|
973
|
-
panel.attributes?.[0] && panel.attributes[1]
|
|
974
|
-
? `${panel.attributes[0]}: media.url,
|
|
975
|
-
${panel.attributes[1]}: media.alt`
|
|
976
|
-
: '';
|
|
977
|
-
|
|
978
|
-
return panel.attributes &&
|
|
979
|
-
panel.attributes[0] &&
|
|
980
|
-
attributes[panel.attributes[0]]
|
|
981
|
-
? `
|
|
982
|
-
<PanelBody title="${panel.title}">
|
|
983
|
-
<PanelRow>
|
|
984
|
-
<div>
|
|
985
|
-
<MediaUpload
|
|
986
|
-
onSelect={ (media) => { setAttributes({ ${mediaAtts} });
|
|
987
|
-
} }
|
|
988
|
-
type="image"
|
|
989
|
-
value={ attributes.${panel.attributes?.[0]} }
|
|
990
|
-
render={({ open }) => (
|
|
991
|
-
<Button variant="secondary" style={{ marginBottom: "20px" }} onClick={ open }>Select Image</Button>
|
|
992
|
-
)}
|
|
993
|
-
/>
|
|
994
|
-
{attributes.${panel.attributes?.[0]} && (
|
|
995
|
-
<img src={attributes.${panel.attributes?.[0]}} alt={attributes.${panel.attributes?.[1]}} />
|
|
996
|
-
)}
|
|
997
|
-
</div>
|
|
998
|
-
</PanelRow>
|
|
999
|
-
</PanelBody>
|
|
1000
|
-
`
|
|
1001
|
-
: '';
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
|
-
const getFormSettingsPanelTemplate = (panel) => {
|
|
1005
|
-
const [formIdAttr, sendEmailAttr] = panel.attributes;
|
|
1006
|
-
|
|
1007
|
-
return `
|
|
1008
|
-
<PanelBody title="${panel.title}" initialOpen={true}>
|
|
1009
|
-
<TextControl
|
|
1010
|
-
label="Form ID"
|
|
1011
|
-
disabled="true"
|
|
1012
|
-
value={attributes.${formIdAttr}}
|
|
1013
|
-
onChange={(val) => setAttributes({ ${formIdAttr}: val })}
|
|
1014
|
-
/>
|
|
1015
|
-
<ToggleControl
|
|
1016
|
-
label="Send Email on Submit"
|
|
1017
|
-
checked={attributes.${sendEmailAttr}}
|
|
1018
|
-
onChange={(val) => setAttributes({ ${sendEmailAttr}: val })}
|
|
1019
|
-
/>
|
|
1020
|
-
</PanelBody>
|
|
1021
|
-
`;
|
|
1022
|
-
};
|
|
1023
|
-
|
|
1024
|
-
const getPhpEmailData = (formIdAttr, fromAttr, toAttr, subjectAttr, messageAttr) => {
|
|
1025
|
-
return `
|
|
1026
|
-
add_action('wp_ajax_send_email_${formIdAttr}', function() {
|
|
1027
|
-
//check_ajax_referer('wp_rest');
|
|
1028
|
-
|
|
1029
|
-
$post_id = $_POST['postId'];
|
|
1030
|
-
|
|
1031
|
-
if (!$post_id) {
|
|
1032
|
-
wp_send_json_error('Missing post ID.');
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
$post_data = $_POST;
|
|
1036
|
-
$post = get_post($post_id);
|
|
1037
|
-
|
|
1038
|
-
if (!$post) {
|
|
1039
|
-
wp_send_json_error('Post not found.');
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
$blocks = parse_blocks($post->post_content);
|
|
1043
|
-
|
|
1044
|
-
$target_block = null;
|
|
1045
|
-
|
|
1046
|
-
foreach ($blocks as $block) {
|
|
1047
|
-
if ($block['blockName'] === '${blockName}') {
|
|
1048
|
-
$target_block = $block;
|
|
1049
|
-
break;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
if (!$target_block) {
|
|
1054
|
-
wp_send_json_error('Block not found.');
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
$attrs = $target_block['attrs'] ?? [];
|
|
1058
|
-
|
|
1059
|
-
$to = sanitize_email(parse_form_placeholders_${functionSufix}($attrs['${toAttr}'] ?? '', $post_data));
|
|
1060
|
-
$from = sanitize_email(parse_form_placeholders_${functionSufix}($attrs['${fromAttr}'] ?? '', $post_data));
|
|
1061
|
-
$subject = sanitize_text_field(parse_form_placeholders_${functionSufix}($attrs['${subjectAttr}'] ?? '', $post_data));
|
|
1062
|
-
$message = wp_kses_post(parse_form_placeholders_${functionSufix}($attrs['${messageAttr}'] ?? '', $post_data));
|
|
1063
|
-
|
|
1064
|
-
if (empty($to) || empty($from)) {
|
|
1065
|
-
wp_send_json_error('Missing email addresses.');
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
$sent = wp_mail($to, $subject, $message, [
|
|
1069
|
-
'Content-Type: text/html; charset=UTF-8',
|
|
1070
|
-
'From: ' . $from
|
|
1071
|
-
]);
|
|
1072
|
-
|
|
1073
|
-
if ($sent) {
|
|
1074
|
-
wp_send_json_success('Email sent');
|
|
1075
|
-
} else {
|
|
1076
|
-
error_log('Failed to send. To: ' . $to . ' | From: ' . $from);
|
|
1077
|
-
wp_send_json_error('Failed to send email.');
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
`;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
const getEmailSettingsPanelTemplate = (panel) => {
|
|
1084
|
-
const [
|
|
1085
|
-
sendEmailAttr,
|
|
1086
|
-
fromAttr,
|
|
1087
|
-
toAttr,
|
|
1088
|
-
subjectAttr,
|
|
1089
|
-
messageAttr,
|
|
1090
|
-
testFeedbackAttr,
|
|
1091
|
-
formIdAttr,
|
|
1092
|
-
] = panel.attributes;
|
|
1093
|
-
|
|
1094
|
-
emailTemplate += sendEmailAttr ? getEmailSaveTemplate(formIdAttr) : '';
|
|
1095
|
-
phpEmailData += getPhpEmailData(formIdAttr, fromAttr, toAttr, subjectAttr, messageAttr);
|
|
1096
|
-
|
|
1097
|
-
return `
|
|
1098
|
-
{ attributes.${sendEmailAttr} && (
|
|
1099
|
-
<PanelBody title="${panel.title}" initialOpen={true}>
|
|
1100
|
-
<TextControl
|
|
1101
|
-
label="From"
|
|
1102
|
-
value={attributes.${fromAttr}}
|
|
1103
|
-
onChange={(val) => setAttributes({ ${fromAttr}: val })}
|
|
1104
|
-
/>
|
|
1105
|
-
<TextControl
|
|
1106
|
-
label="To"
|
|
1107
|
-
value={attributes.${toAttr}}
|
|
1108
|
-
onChange={(val) => setAttributes({ ${toAttr}: val })}
|
|
1109
|
-
/>
|
|
1110
|
-
<TextControl
|
|
1111
|
-
label="Subject"
|
|
1112
|
-
value={attributes.${subjectAttr}}
|
|
1113
|
-
onChange={(val) => setAttributes({ ${subjectAttr}: val })}
|
|
1114
|
-
/>
|
|
1115
|
-
<TextareaControl
|
|
1116
|
-
label="Message Template (HTML Supported)"
|
|
1117
|
-
help="Use {{fieldName}} to insert field values."
|
|
1118
|
-
value={attributes.${messageAttr}}
|
|
1119
|
-
onChange={(val) => setAttributes({ ${messageAttr}: val })}
|
|
1120
|
-
/>
|
|
1121
|
-
|
|
1122
|
-
<Button
|
|
1123
|
-
variant="primary"
|
|
1124
|
-
onClick={() => {
|
|
1125
|
-
const form = document.getElementById('form-${formIdAttr}');
|
|
1126
|
-
const inputs = form.querySelectorAll('input, select, textarea');
|
|
1127
|
-
const body = new URLSearchParams();
|
|
1128
|
-
|
|
1129
|
-
inputs.forEach(input => {
|
|
1130
|
-
if (input.name && !input.disabled) {
|
|
1131
|
-
body.append(input.name, input.value);
|
|
1132
|
-
}
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
body.append('action', 'send_test_email_${functionSufix}');
|
|
1136
|
-
body.append('from', attributes?.${fromAttr} || '');
|
|
1137
|
-
body.append('to', attributes?.${toAttr} || '');
|
|
1138
|
-
body.append('subject', attributes?.${subjectAttr} || '');
|
|
1139
|
-
body.append('message', attributes?.${messageAttr} || '');
|
|
1140
|
-
|
|
1141
|
-
fetch(vars.ajaxUrl, {
|
|
1142
|
-
method: 'POST',
|
|
1143
|
-
body
|
|
1144
|
-
})
|
|
1145
|
-
.then(res => res.json())
|
|
1146
|
-
.then(data => {
|
|
1147
|
-
setAttributes({ ${testFeedbackAttr}: data.success ? 'Test Email Sent!' : data.error });
|
|
1148
|
-
})
|
|
1149
|
-
.catch(() => {
|
|
1150
|
-
setAttributes({ ${testFeedbackAttr}: 'Failed to send test email.' });
|
|
1151
|
-
});
|
|
1152
|
-
}}
|
|
1153
|
-
>
|
|
1154
|
-
Send Test Email
|
|
1155
|
-
</Button>
|
|
1156
|
-
|
|
1157
|
-
{attributes.${testFeedbackAttr} && (
|
|
1158
|
-
<Notice status="info" isDismissible={false}>
|
|
1159
|
-
{attributes.${testFeedbackAttr}}
|
|
1160
|
-
</Notice>
|
|
1161
|
-
)}
|
|
1162
|
-
</PanelBody>
|
|
1163
|
-
)}
|
|
1164
|
-
`;
|
|
1165
|
-
};
|
|
1166
|
-
|
|
1167
|
-
const getEmailSaveTemplate = (formIdAttr) => {
|
|
1168
|
-
return `
|
|
1169
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
1170
|
-
const form = document.getElementById('form-${formIdAttr}');
|
|
1171
|
-
|
|
1172
|
-
form.addEventListener('submit', function(event) {
|
|
1173
|
-
event.preventDefault();
|
|
1174
|
-
|
|
1175
|
-
const inputs = form.querySelectorAll('input, select, textarea');
|
|
1176
|
-
const body = new URLSearchParams();
|
|
1177
|
-
|
|
1178
|
-
inputs.forEach(input => {
|
|
1179
|
-
if (input.name && !input.disabled) {
|
|
1180
|
-
body.append(input.name, input.value);
|
|
1181
|
-
}
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
body.append('action', 'send_email_${formIdAttr}');
|
|
1185
|
-
body.append('postId', vars.postId);
|
|
1186
|
-
|
|
1187
|
-
fetch(vars.ajaxUrl, {
|
|
1188
|
-
method: 'POST',
|
|
1189
|
-
body
|
|
1190
|
-
})
|
|
1191
|
-
.then(res => res.json())
|
|
1192
|
-
.then(data => {
|
|
1193
|
-
console.log(data.success ? 'Test Email Sent!' : data.error);
|
|
1194
|
-
})
|
|
1195
|
-
.catch(() => {
|
|
1196
|
-
console.log('Failed to send test email.');
|
|
1197
|
-
});
|
|
1198
|
-
|
|
1199
|
-
form.reset()
|
|
1200
|
-
});
|
|
1201
|
-
});
|
|
1202
|
-
`;
|
|
1203
|
-
};
|
|
1204
|
-
|
|
1205
|
-
const getHiddenFieldsPanelTemplate = (panel) => {
|
|
1206
|
-
const [hiddenFieldsAttr] = panel.attributes;
|
|
1207
|
-
|
|
1208
|
-
return `
|
|
1209
|
-
<PanelBody title="${panel.title}" initialOpen={false}>
|
|
1210
|
-
{ (attributes.${hiddenFieldsAttr} || []).map((field, index) => (
|
|
1211
|
-
<div key={index} style={{ marginBottom: '10px', borderBottom: '1px solid #ddd', paddingBottom: '10px' }}>
|
|
1212
|
-
<TextControl
|
|
1213
|
-
label="Name"
|
|
1214
|
-
value={field.name}
|
|
1215
|
-
onChange={(val) => {
|
|
1216
|
-
const newFields = [...attributes.${hiddenFieldsAttr}];
|
|
1217
|
-
newFields[index].name = val;
|
|
1218
|
-
setAttributes({ ${hiddenFieldsAttr}: newFields });
|
|
1219
|
-
}}
|
|
1220
|
-
/>
|
|
1221
|
-
<TextControl
|
|
1222
|
-
label="Value"
|
|
1223
|
-
value={field.value}
|
|
1224
|
-
onChange={(val) => {
|
|
1225
|
-
const newFields = [...attributes.${hiddenFieldsAttr}];
|
|
1226
|
-
newFields[index].value = val;
|
|
1227
|
-
setAttributes({ ${hiddenFieldsAttr}: newFields });
|
|
1228
|
-
}}
|
|
1229
|
-
/>
|
|
1230
|
-
<Button
|
|
1231
|
-
variant="secondary"
|
|
1232
|
-
onClick={() => {
|
|
1233
|
-
const newFields = [...attributes.${hiddenFieldsAttr}];
|
|
1234
|
-
newFields.splice(index, 1);
|
|
1235
|
-
setAttributes({ ${hiddenFieldsAttr}: newFields });
|
|
1236
|
-
}}
|
|
1237
|
-
>
|
|
1238
|
-
Remove
|
|
1239
|
-
</Button>
|
|
1240
|
-
</div>
|
|
1241
|
-
))}
|
|
1242
|
-
|
|
1243
|
-
<Button
|
|
1244
|
-
variant="primary"
|
|
1245
|
-
onClick={() => {
|
|
1246
|
-
const newFields = [...(attributes.${hiddenFieldsAttr} || [])];
|
|
1247
|
-
newFields.push({ name: '', value: '' });
|
|
1248
|
-
setAttributes({ ${hiddenFieldsAttr}: newFields });
|
|
1249
|
-
}}
|
|
1250
|
-
>
|
|
1251
|
-
Add Hidden Field
|
|
1252
|
-
</Button>
|
|
1253
|
-
</PanelBody>
|
|
1254
|
-
`;
|
|
1255
|
-
};
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
const findAndGetPanelTemplate = (panel) => {
|
|
1259
|
-
switch (panel.type) {
|
|
1260
|
-
case 'svg':
|
|
1261
|
-
return getSvgPanelTemplate(panel);
|
|
1262
|
-
case 'media':
|
|
1263
|
-
return getMediaPanelTemplate(panel);
|
|
1264
|
-
case 'formSettings':
|
|
1265
|
-
return getFormSettingsPanelTemplate(panel);
|
|
1266
|
-
case 'emailSettings':
|
|
1267
|
-
return getEmailSettingsPanelTemplate(panel);
|
|
1268
|
-
case 'hiddenFields':
|
|
1269
|
-
return getHiddenFieldsPanelTemplate(panel);
|
|
1270
|
-
default:
|
|
1271
|
-
return '';
|
|
1272
|
-
}
|
|
1273
|
-
};
|
|
1274
|
-
|
|
1275
|
-
const getPanelsTemplate = () => {
|
|
1276
|
-
return panels
|
|
1277
|
-
.map((panel) => {
|
|
1278
|
-
return findAndGetPanelTemplate(panel);
|
|
1279
|
-
})
|
|
1280
|
-
.join('\n');
|
|
1281
|
-
};
|
|
1282
|
-
|
|
1283
|
-
const createPanels = () => {
|
|
1284
|
-
return `
|
|
1285
|
-
<Panel>
|
|
1286
|
-
${getPanelsTemplate()}
|
|
1287
|
-
</Panel>`;
|
|
1288
|
-
};
|
|
1289
|
-
|
|
1290
|
-
const buildSaveContent = (editContent) => {
|
|
1291
|
-
return editContent.replace(
|
|
1292
|
-
/<RichText((.|\n)*?)value=\{(.*?)\}((.|\n)*?)\/>/gi,
|
|
1293
|
-
'<RichText.Content value={$3} />'
|
|
1294
|
-
)
|
|
1295
|
-
.replaceAll('class=', 'className=')
|
|
1296
|
-
.replace(
|
|
1297
|
-
/<MediaUpload\b[^>]*>([\s\S]*?(<img\b[^>]*>*\/>)[\s\S]*?)\/>/g,
|
|
1298
|
-
(_match, _attributes, img) => {
|
|
1299
|
-
return img.replace(/onClick={[^}]+}\s*/, '');
|
|
1300
|
-
}
|
|
1301
|
-
);
|
|
1302
|
-
};
|
|
1303
|
-
|
|
1304
|
-
const removeHref = (match) => {
|
|
1305
|
-
return match.replace(/href="(.*?)"/, '');
|
|
1306
|
-
};
|
|
1307
|
-
|
|
1308
|
-
const replaceRichText = (match, group1, _group2, group3) => {
|
|
1309
|
-
return removeHref(match)
|
|
1310
|
-
.replace(group1, '<span')
|
|
1311
|
-
.replace(group3, '</span>');
|
|
1312
|
-
};
|
|
1313
|
-
|
|
1314
|
-
const processLinks = (options) => {
|
|
1315
|
-
let { htmlContent } = options;
|
|
1316
|
-
|
|
1317
|
-
htmlContent = htmlContent
|
|
1318
|
-
? htmlContent.replace(
|
|
1319
|
-
/(<a)[^>]*>([\s\S]*?)(<\/a>)/gim,
|
|
1320
|
-
replaceRichText
|
|
1321
|
-
)
|
|
1322
|
-
: undefined;
|
|
1323
|
-
|
|
1324
|
-
htmlContent = unwrapAnchor(htmlContent);
|
|
1325
|
-
|
|
1326
|
-
return {
|
|
1327
|
-
...options,
|
|
1328
|
-
htmlContent,
|
|
1329
|
-
};
|
|
1330
|
-
};
|
|
1331
|
-
|
|
1332
|
-
const unwrapAnchor = (htmlContent) => {
|
|
1333
|
-
return htmlContent.replace(
|
|
1334
|
-
/<span([^>]*)>\s*<a([^>]*)>(.*?)<\/a>\s*<\/span>/gi,
|
|
1335
|
-
(_, spanAttrs, anchorAttrs, content) => {
|
|
1336
|
-
const allAttrs = {};
|
|
1337
|
-
const attrRegex = /(\S+)=["'](.*?)["']/g;
|
|
1338
|
-
|
|
1339
|
-
let match;
|
|
1340
|
-
while ((match = attrRegex.exec(spanAttrs)) !== null) {
|
|
1341
|
-
allAttrs[match[1]] = match[2];
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
while ((match = attrRegex.exec(anchorAttrs)) !== null) {
|
|
1345
|
-
allAttrs[match[1]] = match[2];
|
|
1346
|
-
}
|
|
1
|
+
export type GeneratedFiles = Record<string, string>;
|
|
2
|
+
|
|
3
|
+
export type JobOutputFile = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
size: number;
|
|
8
|
+
path: string;
|
|
9
|
+
url: string;
|
|
10
|
+
kind: 'source' | 'asset' | 'bundle';
|
|
11
|
+
};
|
|
1347
12
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
13
|
+
export type JobBundle = {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
size: number;
|
|
18
|
+
path: string;
|
|
19
|
+
url: string;
|
|
20
|
+
zipUrl: string;
|
|
21
|
+
};
|
|
1354
22
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
return `${key}:\`${value}\``;
|
|
1362
|
-
}
|
|
1363
|
-
})
|
|
1364
|
-
.join(',\n');
|
|
23
|
+
export type JobManifest = {
|
|
24
|
+
jobId: string;
|
|
25
|
+
status: 'completed';
|
|
26
|
+
output: {
|
|
27
|
+
files: JobOutputFile[];
|
|
28
|
+
bundle: JobBundle;
|
|
1365
29
|
};
|
|
30
|
+
};
|
|
1366
31
|
|
|
1367
|
-
|
|
1368
|
-
|
|
32
|
+
export type NormalizedBlockOptions = {
|
|
33
|
+
title: string;
|
|
34
|
+
name: string;
|
|
35
|
+
slug: string;
|
|
36
|
+
namespace: string;
|
|
37
|
+
prefix: string;
|
|
38
|
+
baseUrl: string | null;
|
|
39
|
+
source: string | null;
|
|
40
|
+
category: string;
|
|
41
|
+
registerCategoryIfMissing: boolean;
|
|
42
|
+
outputPath: string;
|
|
43
|
+
basePath: string;
|
|
44
|
+
writeFiles: boolean;
|
|
45
|
+
shouldSaveFiles: boolean;
|
|
46
|
+
generatePreviewImage: boolean;
|
|
47
|
+
generateIconPreview: boolean;
|
|
48
|
+
jsFiles: string[];
|
|
49
|
+
cssFiles: string[];
|
|
50
|
+
outputMode: 'job' | 'legacy';
|
|
51
|
+
uploadToR2: boolean;
|
|
52
|
+
jobId?: string;
|
|
53
|
+
};
|
|
1369
54
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
55
|
+
export type BlockOptions = {
|
|
56
|
+
title?: string;
|
|
57
|
+
slug?: string;
|
|
58
|
+
baseUrl?: string | null;
|
|
59
|
+
namespace?: string;
|
|
60
|
+
category?: string;
|
|
61
|
+
registerCategoryIfMissing?: boolean;
|
|
62
|
+
outputPath?: string;
|
|
63
|
+
writeFiles?: boolean;
|
|
64
|
+
generatePreviewImage?: boolean;
|
|
65
|
+
jsFiles?: string[];
|
|
66
|
+
cssFiles?: string[];
|
|
67
|
+
outputMode?: 'job' | 'legacy';
|
|
68
|
+
uploadToR2?: boolean;
|
|
69
|
+
jobId?: string;
|
|
70
|
+
name?: string;
|
|
71
|
+
prefix?: string;
|
|
72
|
+
source?: string | null;
|
|
73
|
+
basePath?: string;
|
|
74
|
+
shouldSaveFiles?: boolean;
|
|
75
|
+
generateIconPreview?: boolean;
|
|
76
|
+
};
|
|
1377
77
|
|
|
1378
|
-
|
|
1379
|
-
|
|
78
|
+
export declare const createProfiler: (enabled: boolean) => {
|
|
79
|
+
start(label: string): void;
|
|
80
|
+
end(label: string): void;
|
|
81
|
+
};
|
|
1380
82
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
83
|
+
export declare const findSelfClosingJsxEnd: (
|
|
84
|
+
content: string,
|
|
85
|
+
startIndex: number
|
|
86
|
+
) => number;
|
|
87
|
+
|
|
88
|
+
export declare const replaceSelfClosingJsxComponent: (
|
|
89
|
+
content: string,
|
|
90
|
+
componentName: string,
|
|
91
|
+
replacer: (componentSource: string) => string
|
|
92
|
+
) => string;
|
|
93
|
+
|
|
94
|
+
export declare const getMediaUploadSaveTemplate: (
|
|
95
|
+
image?: {
|
|
96
|
+
randomUrlVariable: string;
|
|
97
|
+
randomAltVariable: string;
|
|
98
|
+
imgClass?: string;
|
|
99
|
+
}
|
|
100
|
+
) => string;
|
|
101
|
+
|
|
102
|
+
export declare const replaceMediaUploadComponents: (
|
|
103
|
+
content: string,
|
|
104
|
+
imageRegistry: Array<{
|
|
105
|
+
randomUrlVariable: string;
|
|
106
|
+
randomAltVariable: string;
|
|
107
|
+
imgClass?: string;
|
|
108
|
+
}>
|
|
109
|
+
) => string;
|
|
110
|
+
|
|
111
|
+
export declare const replaceRichTextComponents: (content: string) => string;
|
|
112
|
+
|
|
113
|
+
export declare const buildAssetExtractionOptions: (
|
|
114
|
+
basePath: string,
|
|
115
|
+
options?: {
|
|
116
|
+
uploadToR2?: boolean;
|
|
117
|
+
returnDetails?: boolean;
|
|
118
|
+
jobId?: string;
|
|
119
|
+
r2Prefix?: string;
|
|
1384
120
|
}
|
|
121
|
+
) => {
|
|
122
|
+
basePath: string;
|
|
123
|
+
saveFile: false;
|
|
124
|
+
verbose: false;
|
|
125
|
+
maxRetryAttempts: 1;
|
|
126
|
+
retryDelay: 0;
|
|
127
|
+
concurrency: 8;
|
|
128
|
+
uploadToR2: boolean;
|
|
129
|
+
returnDetails: boolean;
|
|
130
|
+
jobId?: string;
|
|
131
|
+
r2Prefix?: string;
|
|
132
|
+
};
|
|
1385
133
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
} = settings;
|
|
1392
|
-
|
|
1393
|
-
const iconPreview = generateIconPreview ? `(<img src={vars.url + 'preview.jpeg'} />)` : "'shield'";
|
|
1394
|
-
const edit = await getEdit(settings);
|
|
1395
|
-
const save = buildSaveContent(edit);
|
|
1396
|
-
const blockPanels = createPanels();
|
|
1397
|
-
|
|
1398
|
-
const output = `
|
|
1399
|
-
(function () {
|
|
1400
|
-
${imports}
|
|
1401
|
-
|
|
1402
|
-
registerBlockType('${blockName}', {
|
|
1403
|
-
title: '${name}',
|
|
1404
|
-
icon: ${iconPreview},
|
|
1405
|
-
category: '${category}',
|
|
1406
|
-
attributes: ${parseBlockAttributes()},
|
|
1407
|
-
edit(props) {
|
|
1408
|
-
const { attributes, setAttributes } = props;
|
|
1409
|
-
|
|
1410
|
-
return (
|
|
1411
|
-
<div>
|
|
1412
|
-
<InspectorControls>
|
|
1413
|
-
${blockPanels}
|
|
1414
|
-
</InspectorControls>
|
|
1415
|
-
|
|
1416
|
-
${edit}
|
|
1417
|
-
</div>
|
|
1418
|
-
);
|
|
1419
|
-
},
|
|
1420
|
-
save(props) {
|
|
1421
|
-
const { attributes } = props;
|
|
1422
|
-
|
|
1423
|
-
return (
|
|
1424
|
-
${save}
|
|
1425
|
-
);
|
|
1426
|
-
},
|
|
1427
|
-
});
|
|
1428
|
-
})();`;
|
|
1429
|
-
|
|
1430
|
-
return output;
|
|
1431
|
-
};
|
|
1432
|
-
|
|
1433
|
-
const setupVariables = async (htmlContent, options) => {
|
|
1434
|
-
|
|
1435
|
-
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
1436
|
-
const linkRegex = /<link\b[^>]*((\brel=["']stylesheet["'])|\bhref=["'][^"']+\.css["'])[^>]*>/gi;
|
|
1437
|
-
|
|
1438
|
-
let match;
|
|
1439
|
-
|
|
1440
|
-
htmlContent = htmlContent.replace(styleRegex, (_fullMatch, cssContent) => {
|
|
1441
|
-
styles.push({ type: 'inline', content: cssContent.trim() });
|
|
1442
|
-
return '';
|
|
1443
|
-
});
|
|
1444
|
-
|
|
1445
|
-
const fetchCssPromises = [];
|
|
1446
|
-
while ((match = linkRegex.exec(htmlContent)) !== null) {
|
|
1447
|
-
const url = match[1];
|
|
1448
|
-
const fetchCssPromise = fetch(url)
|
|
1449
|
-
.then((response) => response.text())
|
|
1450
|
-
.then((css) => styles.push({ type: 'external', content: css }))
|
|
1451
|
-
.catch(() => console.warn(`Failed to fetch: ${url}`));
|
|
1452
|
-
fetchCssPromises.push(fetchCssPromise);
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
htmlContent = htmlContent.replace(linkRegex, '');
|
|
1456
|
-
|
|
1457
|
-
await Promise.all(fetchCssPromises);
|
|
1458
|
-
|
|
1459
|
-
css += styles.map((style) => {
|
|
1460
|
-
return `${style.content}`;
|
|
1461
|
-
}).join('\n');
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
console.log('[CSSFETCHED]', css);
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
1469
|
-
const scriptSrcRegex =
|
|
1470
|
-
/<script\s+[^>]*src=["']([^"']+)["'][^>]*>\s*<\/script>/gi;
|
|
1471
|
-
|
|
1472
|
-
let jsMatch;
|
|
1473
|
-
|
|
1474
|
-
htmlContent = htmlContent.replace(scriptRegex, (_fullMatch, jsContent) => {
|
|
1475
|
-
if (jsContent.trim()) {
|
|
1476
|
-
scripts.push({ type: 'inline', content: jsContent });
|
|
1477
|
-
}
|
|
1478
|
-
return '';
|
|
1479
|
-
});
|
|
1480
|
-
|
|
1481
|
-
const fetchJsPromises = [];
|
|
1482
|
-
|
|
1483
|
-
while ((jsMatch = scriptSrcRegex.exec(htmlContent)) !== null) {
|
|
1484
|
-
const url = jsMatch[1];
|
|
1485
|
-
const fetchJsPromise = fetch(url)
|
|
1486
|
-
.then((response) => response.text())
|
|
1487
|
-
.then((js) => scripts.push({ type: 'external', content: js }))
|
|
1488
|
-
.catch(() => console.warn(`Failed to fetch script: ${url}`));
|
|
1489
|
-
fetchJsPromises.push(fetchJsPromise);
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
htmlContent = htmlContent.replace(scriptSrcRegex, '');
|
|
1493
|
-
|
|
1494
|
-
await Promise.all(fetchJsPromises);
|
|
1495
|
-
|
|
1496
|
-
js += scripts.map((script) => script.content).join('\n');
|
|
1497
|
-
|
|
1498
|
-
let {
|
|
1499
|
-
basePath = process.cwd(),
|
|
1500
|
-
cssFiles = [],
|
|
1501
|
-
jsFiles = [],
|
|
1502
|
-
name = 'My block',
|
|
1503
|
-
} = options;
|
|
1504
|
-
|
|
1505
|
-
const newDir = path.join(basePath, replaceUnderscoresSpacesAndUppercaseLetters(name));
|
|
1506
|
-
|
|
1507
|
-
const $ = cheerio.load(htmlContent, {
|
|
1508
|
-
xmlMode: true,
|
|
1509
|
-
decodeEntities: false,
|
|
1510
|
-
});
|
|
1511
|
-
|
|
1512
|
-
$('head, script, style').remove();
|
|
134
|
+
export declare const slugifyBlockValue: (value?: string) => string;
|
|
135
|
+
export declare const formatCategoryLabel: (category?: string) => string;
|
|
136
|
+
export declare const normalizeBlockOptions: (
|
|
137
|
+
options?: BlockOptions
|
|
138
|
+
) => NormalizedBlockOptions;
|
|
1513
139
|
|
|
1514
|
-
|
|
140
|
+
export declare const replaceRelativeUrls: (
|
|
141
|
+
html: string,
|
|
142
|
+
replacer: (url: string) => string
|
|
143
|
+
) => string;
|
|
1515
144
|
|
|
1516
|
-
|
|
145
|
+
export declare const replaceRelativeUrlsInCss: (
|
|
146
|
+
css: string,
|
|
147
|
+
replacer: (url: string) => string
|
|
148
|
+
) => string;
|
|
1517
149
|
|
|
150
|
+
export declare const replaceRelativeUrlsInHtml: (
|
|
151
|
+
html: string,
|
|
152
|
+
baseUrl: string
|
|
153
|
+
) => string;
|
|
1518
154
|
|
|
1519
|
-
|
|
1520
|
-
|
|
155
|
+
export declare const replaceRelativeUrlsInCssWithBase: (
|
|
156
|
+
css: string,
|
|
157
|
+
cssFileUrl: string
|
|
158
|
+
) => string;
|
|
1521
159
|
|
|
1522
|
-
|
|
1523
|
-
...options,
|
|
1524
|
-
jsFiles,
|
|
1525
|
-
cssFiles,
|
|
1526
|
-
htmlContent,
|
|
1527
|
-
basePath: newDir,
|
|
1528
|
-
};
|
|
1529
|
-
} catch (error) {
|
|
1530
|
-
logError(error);
|
|
1531
|
-
}
|
|
1532
|
-
};
|
|
160
|
+
export declare const unwrapBody: (code: string) => string;
|
|
1533
161
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
162
|
+
export declare const transformBlockFile: (
|
|
163
|
+
blockCode: string
|
|
164
|
+
) => { code?: string } | string;
|
|
1538
165
|
|
|
1539
|
-
|
|
1540
|
-
icon(htmlContent, { basePath: path.join(options.basePath, replaceUnderscoresSpacesAndUppercaseLetters(options.name)) });
|
|
1541
|
-
} catch (error) {
|
|
1542
|
-
console.log(`There was an error generating preview. ${error.message}`);
|
|
1543
|
-
}
|
|
166
|
+
export declare const getSnapApiUrl: () => string;
|
|
1544
167
|
|
|
1545
|
-
|
|
1546
|
-
|
|
168
|
+
declare const block: (
|
|
169
|
+
htmlContent: string,
|
|
170
|
+
options?: BlockOptions
|
|
171
|
+
) => Promise<GeneratedFiles | JobManifest>;
|
|
1547
172
|
|
|
1548
|
-
export default block;
|
|
173
|
+
export default block;
|