44 Commits

Author SHA1 Message Date
roman a22a179eff Added nested blocks support (inner_blocks). ACF Only. 2025-05-12 17:28:39 -06:00
roman 95e0cae3ed Merge with latest. 2025-05-11 17:29:57 -06:00
roman 92e4f1ea2d version upgrade 2025-03-29 20:27:56 -06:00
roman 0062bb4506 1.0.33 2025-03-29 20:21:32 -06:00
roman 5dd326be0f Pass $args parameter to content hook. 2025-03-29 20:20:51 -06:00
roman 00efc1677d 1.0.33 2024-12-08 18:44:15 -07:00
roman 38c49a64ad Make sure all images are updated on Global Blocks. 2024-12-08 18:43:39 -07:00
roman 044f721d34 Updated logic of mobile/tablet sizes & scrollbars. 2024-06-23 20:24:57 -06:00
roman 74f4b11ad6 Updated logic of mobile/tablet sizes & scrollbars. 2024-06-23 20:21:44 -06:00
roman 1b2cf2697f Updated logic of mobile/tablet sizes & scrollbars. 2024-06-23 20:18:16 -06:00
roman bcad45d1e8 Temporary disabled ngrok (share live URL option) 2024-06-23 01:26:14 -06:00
roman 3daa725309 Updated logic of Elementor's build. Support Elementor_Widget Trait. 2024-05-24 15:34:12 -06:00
roman 438619c4d9 Added axeweb-block classNames to blocks in Elementor 2024-05-21 12:50:58 -06:00
roman 1b2795fa40 Merge pull request 'Update path to static files of ACF block.' (#13) from wordpress-build into master
Reviewed-on: AXE-WEB/block-dev-tool#13
2024-05-21 09:44:13 +00:00
roman 31786581bd Update path to static files of ACF block. 2024-05-21 03:38:18 -06:00
roman e25f1aa4f1 Merge pull request '1.0.32' (#12) from wordpress-build into master
Reviewed-on: AXE-WEB/block-dev-tool#12
2024-05-21 09:05:32 +00:00
roman f3cd0e5876 1.0.32 2024-05-21 03:03:24 -06:00
roman d6b0ff866b Merge pull request 'Updated the bundle of WordPress block.' (#11) from wordpress-build into master
Reviewed-on: AXE-WEB/block-dev-tool#11
2024-05-21 09:02:22 +00:00
roman 4a7ddf7d29 Updated the bundle of WordPress block. 2024-05-21 03:01:46 -06:00
roman 03a8833f46 Merge pull request 'fix windows archive issue' (#10) from dev into master
Reviewed-on: AXE-WEB/block-dev-tool#10
2024-04-15 12:20:32 +00:00
roman bf8f0e857d fix windows archive issue 2024-04-15 00:07:18 +03:00
roman b76f83d4fa Merge pull request 'dev' (#9) from dev into master
Reviewed-on: AXE-WEB/block-dev-tool#9
2024-01-06 16:16:40 +00:00
roman 977ec0003c Merge branch 'master' into dev
# Conflicts:
#	package-lock.json
#	package.json
2024-01-06 18:15:57 +02:00
roman 0e91c42e89 Update $base_url parameter since the block location changed. 2023-12-15 12:33:26 +02:00
roman 0a4fd3608f Updated path of assets files. 2023-11-13 01:01:35 +02:00
roman 11152dda87 1.0.28 2023-11-06 15:51:40 +02:00
roman 246c9ddd35 Fix capitalization function 2023-11-06 15:51:26 +02:00
roman 164ef7433a 1.0.30 2023-08-27 17:29:05 +03:00
roman a6decb9cfb Remove Block Name from Sync action. 2023-08-27 17:28:54 +03:00
roman 9c85a89a8f Fix issue of blocks folder 2023-08-23 16:55:53 +03:00
roman 58ed3de9dc Make sure we sync versions after each Publish action.
Update versions in JSON files.
2023-08-19 22:38:19 +03:00
roman 3de4a4abb6 1.0.29 2023-08-19 18:09:39 +03:00
roman 256450226b Ask if user wants to download latest version of block on lunch of the devTool. 2023-08-19 18:08:39 +03:00
roman 51db3da072 Fix scrolling issues & swiperjs conflicts. 2023-08-18 23:28:13 +03:00
roman 05c5697bc2 Fit the devTool to nodejs v16. 2023-08-14 16:11:21 +03:00
roman 01e4160040 1.0.28 2023-08-14 15:50:21 +03:00
roman 81f5166043 Print error message to have better understanding of dataObjects errors. 2023-08-14 15:50:12 +03:00
roman a1929fe33c Support missing images folder. 2023-08-13 23:50:52 +03:00
roman 840c002834 Added default layout view. 2023-08-13 23:26:38 +03:00
roman f4474fb7e4 Support work without DevConfigs. 2023-08-13 22:52:45 +03:00
roman 8ca6cb0365 Added "Content-Type" to files that are sent to S3. 2023-08-13 14:27:25 +03:00
roman e2553358be Added support of Project Global Assets. 2023-08-12 21:01:45 +03:00
roman 34dbff5701 Hide "Version" label 2023-06-26 20:05:11 +03:00
roman c408f2f5b3 Added option to generate test variations - updated error messages. 2023-06-25 22:18:44 +03:00
37 changed files with 807 additions and 18611 deletions
-9
View File
@@ -9,17 +9,8 @@ const {isDev, developmentBlockName} = getConfigs();
const blockName = !isDev && config.has('blockName') ? config.get('blockName') : developmentBlockName;
export const PLATFORM_OPTIONS = [{
name: 'wordpress-acf-block',
title: 'WordPress AFC Block'
}, {
name: 'wordpress',
title: 'WordPress'
}, {
name: 'wordpress-component-manager',
title: 'WordPress (Component Manager)'
}, {
name: 'wordpress-elementor',
title: 'WordPress Elementor'
}, {
name: 'hubspot',
title: 'Hubspot'
+193 -34
View File
@@ -9,6 +9,11 @@ import archiver from "archiver";
import {buildWordPress} from "./platforms/wordpress/wordpress-adapter.js";
import {buildHubspotEmail} from "./platforms/hubspot/hubspot-email-adapter.js";
import {buildHubspotPage} from "./platforms/hubspot/hubspot-page-adapter.js";
import fs from "fs/promises";
import {constants} from "fs";
import fetch from "node-fetch";
import mime from "mime-types";
import {exec} from "child_process";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -26,6 +31,7 @@ export async function readJSONFile(jsonFile) {
let data = {};
try {
await fs.access(jsonFile, constants.F_OK | constants.R_OK);
data = await fsExtra.readJson(jsonFile);
} catch (e) {
return {
@@ -44,16 +50,19 @@ function getErrorHtml(message = '', errorMessage = '') {
}
export async function getBlockData(jsonFileName = 'default', {projectPath} = {jsonFileName: 'default'}) {
let data = await readJSONFile(path.join(projectPath, 'data', `${jsonFileName}.json`));
const filePath = path.join(projectPath, 'data', `${jsonFileName}.json`);
const data = await readJSONFile(filePath);
if (data.error) {
return data;
console.log(filePath, data.errorMessage.replace(/<[^>]*>?/gm, ''));
return {};
}
return data;
}
export function getBlockConfigs(args = {modulesPath: '', dataFiles: []}) {
return Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
const updatedConfig = Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
{
projectDir: args.modulesPath, dataFiles: args.dataFiles.map((name) => {
return {
@@ -61,6 +70,14 @@ export function getBlockConfigs(args = {modulesPath: '', dataFiles: []}) {
};
}), remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
});
// Avoid cache conflict on Global Project css/js files.
if (updatedConfig.project) {
updatedConfig.project.css = updatedConfig.project.css ? updatedConfig.project.css + '?cache=' + Date.now() : undefined;
updatedConfig.project.js = updatedConfig.project.js ? updatedConfig.project.js + '?cache=' + Date.now() : undefined;
}
return updatedConfig;
}
export function getBlockName(name = '') {
@@ -98,7 +115,7 @@ export function capitalize(str) {
return str
.toLowerCase()
.split(/[ -_]/g)
.split(/[-_ ]/g)
.filter((word) => !!word)
.map((word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
@@ -109,46 +126,33 @@ export function capitalize(str) {
export async function zipProject(srcDir, outputFileName = 'dist.zip') {
// create a file to stream archive data to.
const output = await fsExtra.createWriteStream(outputFileName);
const archive = archiver('zip', {});
// listen for all archive data to be written
// 'close' event is fired only when a file descriptor is involved
output.on('close', function () {
console.log(archive.pointer() + ' total bytes');
console.log('archiver has been finalized and the output file descriptor has closed.');
return new Promise((resolve, reject) => {
// Create a file to write the archive data
const archive = archiver('zip', {
zlib: {level: 9} // Compression level
});
// This event is fired when the data source is drained no matter what was the data source.
// It is not part of this library but rather from the NodeJS Stream API.
// @see: https://nodejs.org/api/stream.html#stream_event_end
output.on('end', function () {
console.log('Data has been drained');
});
// good practice to catch warnings (ie stat failures and other non-blocking errors)
archive.on('warning', function (err) {
if (err.code === 'ENOENT') {
// log warning
} else {
// throw error
throw err;
}
});
// good practice to catch this error explicitly
// Listen for errors on the archiver
archive.on('error', function (err) {
throw err;
reject(err);
});
// pipe archive data to the file
// Handle closure of the output file stream
output.on('close', function () {
console.log(`Archive created successfully and it is ${archive.pointer()} total bytes`);
resolve();
});
// Pipe archive data to the file
archive.pipe(output);
// append files from a subdirectory, putting its contents at the root of archive
// Add files to the archive
archive.directory(srcDir, false);
// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
await archive.finalize();
// Finalize the archive - this is very important to ensure the file stream is finished
archive.finalize();
});
}
export async function buildExportFiles(blockName, platform) {
@@ -160,3 +164,158 @@ export async function buildExportFiles(blockName, platform) {
await buildHubspotPage(blockName)
}
}
export function removeCommentsFromCss(content) {
return content.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '');
}
export function removeCommentsFromJs(content) {
return content.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '');
}
export async function uploadFile(filePath, uploadUrl, validator) {
const options = {};
const contentType = mime.lookup(filePath);
if (['text/css', 'application/javascript'].includes(contentType)) {
options.encoding = 'utf8';
}
let body = await fs.readFile(filePath, options);
if (validator) {
body = validator(body);
}
try {
const response = await fetch(uploadUrl, {
method: 'PUT',
body: body,
headers: {'Content-Type': contentType}
});
return response.status !== 200;
} catch (err) {
console.log(err)
const fileName = filePath.split('/').pop();
throw new Error(`Can't upload "${fileName}" file. Server permission error.`);
}
}
export async function getImagesList(rootFolder, subFolder = '') {
const imagesPath = path.join(rootFolder, subFolder);
try {
await fs.access(imagesPath);
} catch (err) {
// Folder doesn't exist.
return [];
}
const images = await fs.readdir(imagesPath);
const imagesList = [];
for (const image of images) {
const stats = await fs.stat(path.join(imagesPath, image));
if (stats.isDirectory()) {
const subImages = await getImagesList(rootFolder, image);
imagesList.push(...subImages);
} else {
imagesList.push(path.join(subFolder, image));
}
}
return imagesList;
}
export async function isFileEmpty(filePath, ignoreComments = false) {
const fileContent = await fs.readFile(filePath, 'utf8');
const lines = fileContent.split('\n');
for (const line of lines) {
if (ignoreComments && line.trim().startsWith('//')) {
continue;
}
if (line.trim().length > 0) {
return false;
}
}
return true;
}
export function replaceNames(content, images, uploadedImages) {
images.forEach((image, index) => {
content = content.replaceAll(image, uploadedImages[index].fileName);
});
return content;
}
export async function getBlockFromCloud(blockName, blocksRegistry) {
const queryString = new URLSearchParams();
queryString.append('blockName', blockName);
queryString.append('includeDevConfig', 'true');
const response = await fetch(`${blocksRegistry}?${queryString.toString()}`);
const responseData = await response.json();
if (!responseData || !responseData.name) {
const message = "⚠️ Block not found, please contact administrator."
throw new Error(message);
}
if (responseData.statusCode && responseData.statusCode !== 200) {
const message = ["⚠️ [ERROR]", responseData.message || "Server side error."].join(' ');
throw new Error(message);
}
return responseData;
}
export async function verifyVersion(projectPath, blocksRegistry) {
const blockJson = await readJSONFile(path.join(projectPath, `block.json`));
const blockName = getBlockName(blockJson.name);
if (typeof blockJson.version === 'undefined' || !blockName.name) {
return true;
}
/*
* This block is managed on cloud.
* Let's detect the latest version.
*/
const block = await getBlockFromCloud('@' + blockJson.name, blocksRegistry);
return block.version === blockJson.version;
}
export async function syncFilesWithCloud(jsonBlockPath, bs, sourceFiles = false) {
const blockJson = await readJSONFile(jsonBlockPath);
const blockName = blockJson.name.startsWith('@') ? blockJson.name : `@${blockJson.name}`;
bs.pause();
// Looks like it takes time to pause the browser-sync server, so delay(setTimeout) is necessary.
await new Promise((resolve) => setTimeout(() => resolve(), 1000));
await new Promise((resolve) => {
const args = sourceFiles ? '--source' : '';
const createBlockModulePath = `./node_modules/@axe-web/create-block`;
// exec(`BLOCK_NAME=${blockName} node ${createBlockModulePath}/create-block.js sync ${args}`, (err, stdout, stderr) => {
exec(`node ${createBlockModulePath}/create-block.js sync ${args}`, (err, stdout, stderr) => {
if (err || stderr) {
const message = err || stderr;
console.error('Error:', message);
throw new Error(message);
}
console.log(stdout);
resolve();
});
});
bs.resume();
}
+2
View File
@@ -3,6 +3,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/swiper@8.4.5/swiper-bundle.min.css"/>
{{#if config.project.css }} <link rel="stylesheet" href="{{ config.project.css }}">
{{/if}}
{{#if config.cssUrl }}
{{#each config.cssUrl }}
<link rel="stylesheet" href="{{ this }}">
+4 -1
View File
@@ -2,7 +2,10 @@
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
<script src="/scripts/dist/frame-index.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://unpkg.com/swiper@8.4.5/swiper-bundle.min.js"></script>{{#if config.jsUrl }}
<script src="https://unpkg.com/swiper@8.4.5/swiper-bundle.min.js"></script>
{{#if config.project.js }}<script src="{{ config.project.js }}"></script>
{{/if}}
{{#if config.jsUrl }}
{{#each config.jsUrl }}<script src="{{ this }}"></script>
{{/each}}
{{/if}}
+2 -2
View File
@@ -3,8 +3,8 @@
<div class="page_toolbar__left"></div>
<div class="page_toolbar__middle">
<div>
Version: <b>1rem = {{ config.version }}px</b>
<div style="display: none">
Version: {{ config.version }}
</div>
</div>
+1 -167
View File
@@ -1,167 +1 @@
window.initBlock = initBlock;
let template;
let data = {};
let reload;
// Blocks Initialization.
function initBlock(blockName = '', selector = '', cb) {
reload = function () {
document.querySelectorAll(selector).forEach((el) => cb(el));
};
reload();
}
// Scrollbars / Frame resizes notifications.
(function () {
let height;
handleHeightChange(); // Initial frame's height setup.
setupResizeListener(); // Listen to frame's height changes.
///
function setupResizeListener() {
const resizeObserver = new ResizeObserver(handleHeightChange);
resizeObserver.observe(document.body);
}
function handleHeightChange() {
const updatedHeight = getCurrentHeight();
if (height === updatedHeight) {
return;
}
const RESIZE_CODE = 'resize:';
height = updatedHeight;
window.parent.postMessage(RESIZE_CODE + JSON.stringify({height}), '*');
}
function getCurrentHeight() {
return document.querySelector('#hbs-container').scrollHeight;
}
})();
// Data Updates Listeners.
(function () {
loadDataOptions();
listenToDataOptionsUpdates();
function listenToDataOptionsUpdates() {
window.addEventListener('message', function (event) {
const message = event.data;
const prefix = 'dataUpdate:';
if (typeof message !== "string" || !message.startsWith(prefix)) {
return;
}
try {
data = JSON.parse(message.substring(prefix.length));
updateBlock({data});
} catch (e) {
console.log('Error parsing incoming data.', e);
}
});
}
function getQueryParams() {
const urlParams = new URLSearchParams(window.location.search);
const params = {};
for (const [key, value] of urlParams) {
params[key] = value;
}
return params;
}
function loadDataOptions() {
const queryParameters = new URLSearchParams({name: getQueryParams().data || 'default'});
fetch(`/data?${queryParameters}`)
.then((response) => response.json())
.then((response) => {
data = response.data; // Update state.
updateBlock({data});
});
}
})();
// Listen to Template updates.
(function () {
initSocket();
function initSocket() {
const socket = window.io.connect();
socket.on('error', function (err) {
console.log(err);
});
// socket.on('connect', function () {
// console.log('user connected', socket.id)
// });
socket.on('templateUpdate', function (args) {
updateBlock({template: args.template});
});
}
})();
function updateBlock(args = {}) {
if (args.template) {
template = args.template; // Update state.
}
if (args.data) {
data = args.data; // Update state.
}
if (!template) {
return;
}
renderBlock(template, data || {}, document.getElementById("hbs-container"));
}
function renderBlock(templateHbs, jsonData, target) {
const template = Handlebars.compile(templateHbs);
/**
* Handlebars Helpers
*/
Handlebars.registerHelper('esc_attr', function (attr) {
return attr;
});
Handlebars.registerHelper('esc_url', function (attr) {
return attr;
});
Handlebars.registerHelper('esc_html', function (attr) {
return attr;
});
Handlebars.registerHelper('base_url', function () {
return '/';
});
let html;
try {
html = template(jsonData);
} catch (e) {
html = `<div style="max-width: 1280px; margin: 1rem auto;">
<h1 style="all: unset; font-size: 1.5rem; font-weight: bold; display: block;">Syntax Error:</h1>
<pre style="all: unset; padding: 10px 15px; background-color: #ffe6e6; border: 1px solid red; border-radius: 0.25rem; overflow: auto; display: block; white-space: pre;">${e.toString()}</pre>
</div>`;
}
target.innerHTML = html;
if (reload) {
reload();
}
}
//# sourceMappingURL=frame-index.min.js.map
let e;window.initBlock=function(e="",n="",r){t=function(){document.querySelectorAll(n).forEach((e=>r(e)))},t()};let t,n={};function r(r={}){r.template&&(e=r.template),r.data&&(n=r.data),e&&function(e,n,r){const a=Handlebars.compile(e);let o;Handlebars.registerHelper("esc_attr",(function(e){return e})),Handlebars.registerHelper("esc_url",(function(e){return e})),Handlebars.registerHelper("esc_html",(function(e){return e})),Handlebars.registerHelper("base_url",(function(){return"/"})),Handlebars.registerHelper("inner_blocks",(function(e){const t=e.data.root.inner_blocks;return t?new Handlebars.SafeString(t):""}));try{o=a(n)}catch(e){o=`<div style="max-width: 1280px; margin: 1rem auto;">\n <h1 style="all: unset; font-size: 1.5rem; font-weight: bold; display: block;">Syntax Error:</h1>\n <pre style="all: unset; padding: 10px 15px; background-color: #ffe6e6; border: 1px solid red; border-radius: 0.25rem; overflow: auto; display: block; white-space: pre;">${e.toString()}</pre>\n </div>`}r.innerHTML=o,t&&t()}(e,n||{},document.getElementById("hbs-container"))}!function(){function e(){const e=new URLSearchParams(window.location.search),t={};for(const[n,r]of e)t[n]=r;return t}!function(){const t=new URLSearchParams({name:e().data||"default"});fetch(`/data?${t}`).then((e=>e.json())).then((e=>{n=e.data,r({data:n})}))}(),window.addEventListener("message",(function(e){const t=e.data,a="dataUpdate:";if("string"==typeof t&&t.startsWith(a))try{n=JSON.parse(t.substring(a.length)),r({data:n})}catch(e){console.log("Error parsing incoming data.",e)}}))}(),function(){const e=window.io.connect();e.on("error",(function(e){console.log(e)})),e.on("templateUpdate",(function(e){r({template:e.template})}))}();
File diff suppressed because one or more lines are too long
+15 -9917
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-21
View File
@@ -1,21 +0,0 @@
export function setupFrameResizeListener() {
const previewFrame = getPreviewFrame();
window.addEventListener('message', function (e) {
const RESIZE_CODE = 'resize:';
if (typeof e.data !== 'string' || !e.data.startsWith(RESIZE_CODE)) {
return;
}
const data = JSON.parse(e.data.substring(RESIZE_CODE.length))
let height = Number.parseInt(data.height)
if (height > 20000) {
height = 20000; // Limit max height.
}
previewFrame.style.height = height + 'px'
});
}
export function getPreviewFrame() {
return document.getElementById('preview_frame');
}
+9 -40
View File
@@ -15,46 +15,6 @@ function initBlock(blockName = '', selector = '', cb) {
reload();
}
// Scrollbars / Frame resizes notifications.
(function () {
let height;
const debug = false;
handleHeightChange(); // Initial frame's height setup.
setupResizeListener(); // Listen to frame's height changes.
///
function setupResizeListener() {
const resizeObserver = new ResizeObserver(handleHeightChange);
resizeObserver.observe(document.body);
}
function handleHeightChange() {
const updatedHeight = getCurrentHeight();
if (debug) {
console.log('Height Updates', 'Old vs New: ' + height, updatedHeight);
}
if (height === updatedHeight) {
return;
}
const RESIZE_CODE = 'resize:';
height = updatedHeight;
window.parent.postMessage(RESIZE_CODE + JSON.stringify({height}), '*');
if (debug) {
console.log('Resize message sent: ', height)
}
}
function getCurrentHeight() {
return document.querySelector('#hbs-container').scrollHeight;
}
})();
// Data Updates Listeners.
(function () {
loadDataOptions();
@@ -159,6 +119,15 @@ function renderBlock(templateHbs, jsonData, target) {
return '/';
});
Handlebars.registerHelper('inner_blocks', function (options) {
const content = options.data.root['inner_blocks'];
if (!content) {
return '';
}
return new Handlebars.SafeString(content);
});
let html;
try {
+5 -2
View File
@@ -3,13 +3,16 @@
import {setupResponsiveness} from './toolbar/responsive.jsx';
import {setupPublish} from "./toolbar/publish.jsx";
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
import {getPreviewFrame, setupFrameResizeListener} from "./frame/editor.js";
const rootAttributes = {
previewFrame: getPreviewFrame(),
};
setupFrameResizeListener();
// setupFrameResizeListener();
setupResponsiveness(rootAttributes);
setupDataOptions(rootAttributes);
setupPublish(rootAttributes);
function getPreviewFrame() {
return document.getElementById('preview_frame');
}
+100
View File
@@ -0,0 +1,100 @@
'use strict';
import React, {useState} from "react";
import * as ReactDOM from "react-dom/client";
function init() {
const wrapper = document.querySelector('#screen');
const root = ReactDOM.createRoot(wrapper);
const html = (<SyncScreen/>);
root.render(html);
}
function SyncScreen() {
const [loading, setLoading] = useState(null);
const [error, setError] = useState(null);
return <>
<section className="container py-5">
<h1 style={{marginBottom: '2rem'}}>Oops... Block not in sync.</h1>
{error && <p className="alert alert-danger">{error}</p>}
{loading ?
<>
{loading === 'syncing' &&
<p>Version upgrade in progress...</p>
}
<p>Please wait...</p>
</>
:
<>
<p>Your version of the block is not in sync with the cloud (not latest version).<br/>
Would you like to update it?</p>
<div className="options">
<button className="btn btn-primary" style={{marginRight: '0.5rem'}} onClick={runSyncLogic}>Yes, Update to
Latest
</button>
<button className="btn btn-secondary" onClick={ignoreVersionSync}>Ignore</button>
</div>
</>
}
</section>
</>
async function ignoreVersionSync() {
setLoading(true);
let data = {};
try {
const response = await fetch('/sync', {
method: 'POST',
body: JSON.stringify({ignore: true}),
headers: {
'Content-Type': 'application/json'
}
});
data = await response.json();
} catch (err) {
setError('Error: ' + err.message);
setLoading(false);
return;
}
if (data.status !== 200) {
setError(data.message);
setLoading(false);
return;
}
setTimeout(() => window.location.reload(), 1000);
}
async function runSyncLogic() {
setLoading('syncing');
let data = {};
try {
const response = await fetch('/sync', {method: 'POST'});
data = await response.json();
} catch (err) {
setError('Error: ' + err.message);
setLoading(false);
return;
}
if (data.status !== 200) {
setError(data.message);
setLoading(false);
return;
}
setTimeout(() => window.location.reload(), 1000);
}
}
init();
@@ -125,12 +125,13 @@ function DataOptions(props = {}) {
return fetch(url, requestOptions)
.then(response => response.json())
.then(result => {
console.log(result)
if (result.statusCode !== 200) {
throw new Error(result.message);
}
const data = result.variation;
updateState({dataText: JSON.stringify(data, null, 2), data});
updateState({dataText: JSON.stringify(data, null, 2), data, errorMessage: null, loading: false});
updateIframe(data);
})
.catch(error => {
@@ -130,7 +130,8 @@ export function DesignPreview({previewOption = {widthDimension: 0}}) {
return 0;
}
const scrollbarOffset = 7;
// const scrollbarOffset = 7; // Looks like browser scrollbar is not included in the width calculation anymore.
const scrollbarOffset = 0;
return window.innerWidth / 2 - previewOption.widthDimension / 2 - scrollbarOffset;
}
+3 -1
View File
@@ -65,7 +65,9 @@ function Responsive(props = {}) {
function updateController() {
let frameBreakpoint = breakpoint;
if (typeof frameBreakpoint !== 'string') {
frameBreakpoint = frameBreakpoint + 'px';
// const scrollbarWidth = 15; // Looks like browser's logic was changed and now scrollbar is not included in the width.
const scrollbarWidth = 0;
frameBreakpoint = (scrollbarWidth + frameBreakpoint) + 'px';
}
previewFrame.style.setProperty('--breakpoint', frameBreakpoint);
+1 -5
View File
@@ -1,5 +1,4 @@
.preview {
overflow-y: scroll;
height: calc(100% - var(--top_panel_height));
position: relative;
}
@@ -9,17 +8,14 @@
margin-right: auto;
margin-left: auto;
min-height: 100%;
//height: 100%;
--top_spacing: 0px;
--breakpoint_top_spacing: 30px;
margin-top: var(--top_spacing);
height: 100%;
background-color: white;
border: 0;
outline: 1px solid #E2E8F0;
transition: max-width .3s ease-in-out, width .3s ease-in-out, margin-top .3s ease-in-out;
&.has-breakpoint {
-9
View File
@@ -1,9 +0,0 @@
.swiper {
&-slide {
width: initial;
}
&-wrapper {
height: initial;
}
}
-3
View File
@@ -114,7 +114,6 @@ body {
}
.preview {
overflow-y: scroll;
height: calc(100% - var(--top_panel_height));
position: relative;
}
@@ -127,10 +126,8 @@ body {
--top_spacing: 0px;
--breakpoint_top_spacing: 30px;
margin-top: var(--top_spacing);
height: 100%;
background-color: white;
border: 0;
outline: 1px solid #E2E8F0;
transition: max-width 0.3s ease-in-out, width 0.3s ease-in-out, margin-top 0.3s ease-in-out;
}
#preview_frame.has-breakpoint {
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["page--main.scss","_buttons.scss","_overlay.scss","_page--preview.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EAEA;;;ACTF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;;AC5BJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AFIF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;AAKN;EAEE;EACA;EACA;EACA;;AAGF;EACE;;;AGlFJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;;AAEA;EACE;EAEA;EACA;;;AH6DJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;;;AAGF;EAEE;EACA","file":"page--main.css"}
{"version":3,"sourceRoot":"","sources":["page--main.scss","_buttons.scss","_overlay.scss","_page--preview.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EAEA;;;ACTF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;;AC5BJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AFIF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;AAKN;EAEE;EACA;EACA;EACA;;AAGF;EACE;;;AGlFJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EACA;EACA;;AAEA;EACE;EAEA;EACA;;;AHiEJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;;;AAGF;EAEE;EACA","file":"page--main.css"}
+1 -17
View File
@@ -2,24 +2,8 @@ body {
margin: 0;
}
main {
margin-left: auto;
margin-right: auto;
min-height: 100%;
overflow-x: hidden;
}
main .swiper-slide {
width: initial;
}
main .swiper-wrapper {
height: initial;
}
.body--iframe {
overflow-y: hidden;
}
.body--iframe main {
overflow-y: auto;
overflow-y: scroll;
}
.fullscreen_layout {
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["page--view.scss","_page--view-swiper.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;;ACPA;EACE;;AAGF;EACE;;;ADSJ;EACE;;AAEA;EAGE;;;AAIJ;EACE;EACA;EACA;EACA","file":"page--view.css"}
{"version":3,"sourceRoot":"","sources":["page--view.scss"],"names":[],"mappings":"AAAA;EACE;;;AAIF;EACE;;;AAGF;EACE;EACA;EACA;EACA","file":"page--view.css"}
+1 -18
View File
@@ -2,25 +2,9 @@ body {
margin: 0;
}
main {
margin-left: auto;
margin-right: auto;
min-height: 100%;
overflow-x: hidden;
// Fixes scrolling issues of swiperJS. Should be included in all projects.
@import "page--view-swiper";
}
// iFrame mode
.body--iframe {
overflow-y: hidden;
main {
// If you change to "overflow: initial", the margin-top/bottom of first/last element will be not included.
// Test on fresh block setup where heading has margin-top.
overflow-y: auto;
}
overflow-y: scroll;
}
.fullscreen_layout {
@@ -29,4 +13,3 @@ main {
background-image: url('https://i.ibb.co/pjwL8D1/shapelined-JBKdviwe-XI-unsplash.jpg');
background-size: cover;
}
+18
View File
@@ -0,0 +1,18 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Block Development Tool</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
</head>
<body>
<div id="screen">Loading...</div>
<script src="/socket.io/socket.io.js"></script>
<script src="/scripts/dist/sync.min.js"></script>
</body>
</html>
+116 -8123
View File
File diff suppressed because it is too large Load Diff
+8 -5
View File
@@ -1,16 +1,16 @@
{
"name": "@axe-web/block-dev-tool",
"version": "1.0.27",
"version": "1.0.36",
"author": {
"name": "AXE-WEB",
"email": "office@axe-web.com",
"url": "https://axe-web.com/"
},
"scripts": {
"info": "NODE_ENV=development BLOCK_NAME=header MODULE_PATH= node debug.js",
"dev": "NODE_ENV=development BLOCK_NAME=header MODULE_PATH= node server.js",
"build-platform": "NODE_ENV=development BLOCK_NAME=header MODULE_PATH= node ./build.js",
"dev-dev-tool": "NODE_ENV=development rollup --config rollup.config.js --watch",
"info": "BLOCK_NAME=ebooks-resources MODULE_PATH= node debug.js",
"dev": "BLOCK_NAME=ebooks-resources MODULE_PATH= node server.js",
"build-platform": "BLOCK_NAME=ebooks-resources MODULE_PATH= node ./build.js",
"dev-dev-tool": "rollup --config rollup.config.js --watch",
"build-dev-tool": "rollup --config rollup.config.js"
},
"engines": {
@@ -19,8 +19,10 @@
"license": "ISC",
"type": "module",
"dependencies": {
"@axe-web/create-block": "^1.1.30",
"@braintree/sanitize-url": "^6.0.0",
"archiver": "^5.3.1",
"body-parser": "^1.20.2",
"browser-sync": "^2.27.9",
"config": "^3.3.7",
"escape-html": "^1.0.3",
@@ -37,6 +39,7 @@
"lodash-es": "^4.17.21",
"mem-fs": "^2.2.1",
"mem-fs-editor": "^9.5.0",
"mime-types": "^2.1.35",
"ngrok": "^5.0.0-beta.2",
"node-fetch": "^3.2.10",
"open": "^8.4.0",
@@ -26,6 +26,10 @@ export async function createDistFolder(blockName, projectPath = '') {
}
export function getBlockFields(block = {}, type = 'content') {
if (!block['field_groups']) {
return [];
}
const fields_group = block['field_groups'].find((group) => group.name === type);
const fields = [];
+4 -10
View File
@@ -35,14 +35,6 @@ trait Custom_Handlebars {
return $context;
} );
$this->add_handlebar( 'safe_html', function ( $context ) {
if ( function_exists( 'wp_kses_post' ) ) {
return wp_kses_post( $context );
}
return $context;
} );
}
public function add_handlebar( $key, $func ): void {
@@ -71,8 +63,8 @@ class Component_Builder {
$this->register_default_handlebar_helpers();
$this->add_handlebar( 'base_url', function ( $context ) {
$path = join( '/', [ 'blocks', $this->block_project, $this->block_name, 'templates' ] );
return plugins_url( $path . '/', 'axeweb-blocks-library/axeweb-blocks-library.php' );
$path = join( '/', [ $this->block_project, $this->block_name, 'templates' ] );
return join( '/', [ get_site_url(),'wp-content', 'axe-web-blocks', $path, '' ] );
} );
}
@@ -91,6 +83,8 @@ class Component_Builder {
private function get_handlebars_template( $path = '' ): string {
$template = file_get_contents( $path );
$template = preg_replace( '/{{inner_blocks}}/', '<InnerBlocks/>', $template );
$phpStr = LightnCandy::compile( $template,
[
'flags' => Flags::FLAG_NOESCAPE | Flags::FLAG_PARENT | Flags::FLAG_SPVARS | Flags::FLAG_ELSE | Flags::FLAG_JSLENGTH | Flags::FLAG_JSTRUE | Flags::FLAG_THIS,
@@ -2,19 +2,13 @@
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor) { %>extends \Core\Component <% } %>{
class <%= blockClassModel %>_Component extends \Core\Component {
const VERSION = '<%= version %>';
public $block_project = '<%= ownerFilename %>';
public $block_name = '<%= blockFilename %>';
public $hook_prefix = 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>';
<% if (!isComponentManager && !isElementor) { %>public function __construct() {
parent::__construct();
// add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] );
add_action( 'after_setup_theme', [ $this, 'register_assets' ] );
}
<% } %>function register_assets(): void {
function register_assets(): void {
$base_path = plugin_dir_url( __FILE__ ); // In Plugins
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/'; // In Theme
@@ -22,12 +16,14 @@ class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor)
wp_register_style( 'block-<%= blockFilename %>', $base_path . 'templates/styles/<%= blockFilename %>.min.css', $style_deps, self::VERSION );
$script_deps = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::script_deps', [ 'assets-script' ] );
wp_register_script( 'block-<%= blockFilename %>', $base_path . 'templates/scripts/<%= blockFilename %>.min.js', $script_deps, self::VERSION, true );<% if (!isElementor) { %>
wp_register_script( 'block-<%= blockFilename %>', $base_path . 'templates/scripts/<%= blockFilename %>.min.js', $script_deps, self::VERSION, true );
}
public function prepare_for_print() {
if ( ! is_admin() ) {
wp_enqueue_style( 'block-<%= blockFilename %>' );
wp_enqueue_script( 'block-<%= blockFilename %>' );
}<% } %>
}
}
public function get_content( $args = [] ): string {
@@ -37,8 +33,8 @@ class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor)
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( $args, self::class );
return apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::content', $output );
}<% if (isElementor) { %>
return apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::content', $output, $args );
}
function register_custom_logic(): void {
add_action( 'elementor/widgets/widgets_registered', [ $this, 'register_elementor_widget' ] );
@@ -47,9 +43,25 @@ class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor)
function register_elementor_widget( $widgets_manager ): void {
require_once "helpers/<%= blockClassModel %>_Elementor_Widget.php";
$widgets_manager->register_widget_type( new Helpers\<%= blockClassModel %>_Elementor_Widget() );
}<% } %>
}
function register_acf_block() {
$this->register_block( __DIR__ . "/<%= blockFilename %>.block.json", [
'style_assets' => [
[
'name' => 'block-<%= blockFilename %>',
'url' => $this->get_assets_path_url( 'templates/styles/<%= blockFilename %>.min.css' ),
]
],
'script_assets' => [
[
'name' => 'block-<%= blockFilename %>',
'url' => $this->get_assets_path_url( 'templates/scripts/<%= blockFilename %>.min.js' ),
]
]
] );
}
}
<% if (isComponentManager) { %><%= blockClassModel %>_Component::get_instance();<% } else {
%>new <%= blockClassModel %>_Component();<% } %>
<%= blockClassModel %>_Component::get_instance();
// new <%= blockClassModel %>_Component();
@@ -1,62 +0,0 @@
<?php
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
class <%= blockClassModel %>_Component extends \Core\Component {
const VERSION = '<%= version %>';
public $block_project = '<%= ownerFilename %>';
public $block_name = '<%= blockFilename %>';
public $hook_prefix = 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>';
public function get_content( $args = [] ): string {
$default_args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::default_args', [] ); // Not really practical.
$args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::prepare_args', array_merge( $default_args, $args ) );
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( $args, self::class );
return apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::content', $output );
}
<% if (!include_acf_block && !include_native_gutenberg_block) { %>function register_assets(): void {
$version = get_plugin_data( __DIR__ . "/../../scytale-custom-blocks.php" )['Version']; // In Plugins
// $version = \Core\Global_Functions::get_current_version_number(); // In Theme
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/';
wp_enqueue_style( 'block-<%= blockFilename %>', plugins_url( 'templates/styles/<%= blockFilename %>.min.css', __FILE__ ), ['assets-style'], self::VERSION );<% if (include_script) { %>
wp_enqueue_script( 'block-<%= blockFilename %>', plugins_url( 'templates/scripts/<%= blockFilename %>.min.js', __FILE__ ), ['assets-script'], self::VERSION, true );<% } %>
wp_enqueue_script( 'script-block-<%= blockFilename %>' );
}<% } %>
<% if (include_acf_block) { %> function register_acf_block() {
$this->register_block( __DIR__ . "/<%= blockFilename %>.block.json", [
'style_assets' => [
[
'name' => '<%= blockFilename %>',
'url' => plugins_url( 'templates/styles/<%= blockFilename %>.min.css', __FILE__ ),
]
],
'script_assets' => [
[
'name' => '<%= blockFilename %>',
'url' => plugins_url( 'templates/scripts/<%= blockFilename %>.min.js', __FILE__ ),
]
]
] );
}
<% } %><% if (include_native_gutenberg_block) { %> function register_native_gutenberg_block() {
register_block_type( __DIR__ . '/templates/gutenberg-block/block.json' );<% if (include_script) { %>
add_action( 'wp_enqueue_scripts', function () {
$asset_file_front = include( plugin_dir_path( __FILE__ ) . '/templates/gutenberg-block/build/front.asset.php' );
wp_enqueue_script(
'gutenberg-<%= blockFilename %>-scripts-front',
plugins_url( 'templates/gutenberg-block/build/front.js', __FILE__ ),
$asset_file_front['dependencies'],
$asset_file_front['version'],
true
);
} );<% } %>
}<% } %>
}
<%= blockClassModel %>_Component::get_instance();
@@ -1,14 +0,0 @@
<?php
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
class <%= blockClassModel %>_API {
static function prepare_args( $args = [] ) {
// $args = array_merge( [], $args );
return $args;
}
}
@@ -1,17 +0,0 @@
<?php
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
/**
* Component's default args.
*/
class <%= blockClassModel %>_Defaults {
public static function default_args(): array {
$args = <%- defaultData %>;
// $args['base_url'] = \Core\Global_Functions::get_file_url( __DIR__ . '/../templates/' );
return $args;
}
}
@@ -3,6 +3,8 @@
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
class <%= blockClassModel %>_Elementor_Widget extends \Elementor\Widget_Base {
use \Core\Traits\Elementor_Widget;
const PLUGIN_NAME = '<%= blockFilename %>';
public function get_name() {
@@ -35,12 +37,8 @@ class <%= blockClassModel %>_Elementor_Widget extends \Elementor\Widget_Base {
$component = new \AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\<%= blockClassModel %>_Component();
$args = self::prepare( $settings );
$args = $this->prepare( $settings );
$component->render( $args );
}
public static function prepare( $args ): array {
// Prepare $args for render function.
return $args;
}
}
+9 -39
View File
@@ -1,5 +1,5 @@
import path from "path";
import {mkdir, copyFile} from "fs/promises";
import fs, {mkdir, copyFile} from "fs/promises";
import {capitalize, createFiles, getBlockName, getConfigs, readJSONFile} from "../../helpers.js";
import {fileURLToPath} from 'url';
import {copy} from "fs-extra";
@@ -10,10 +10,6 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export async function buildWordPress(blockName, args = {}) {
const isBlock = args.platform === 'wordpress-acf-block';
const isElementor = args.platform === 'wordpress-elementor';
const isComponentManager = args.platform === 'wordpress-component-manager'
const {modulesPath, projectPath} = getConfigs();
const distPath = path.join(projectPath, 'exports', args.platform);
@@ -37,12 +33,7 @@ export async function buildWordPress(blockName, args = {}) {
ownerClass: owner.replace(/ /ig, '_'),
ownerFilename: owner.toLowerCase().replace(/ /ig, '-'),
templateFormat: 'php',
include_acf_block: isBlock,
include_native_gutenberg_block: false,
include_script: true,
include_elementor_widget: isElementor,
isElementor,
isComponentManager,
});
await copyFile(blockFilePath, path.join(distPath, data.blockFilename + '.block.json'));
@@ -68,24 +59,22 @@ export async function buildWordPress(blockName, args = {}) {
path.join(distPath, 'templates', 'scripts', `${data.blockFilename}.min.js`),
);
try {
const imagesPath = path.join(projectPath, 'src', 'images');
await fs.access(imagesPath);
await copy(
path.join(projectPath, 'src', 'images'),
path.join(imagesPath),
path.join(distPath, 'templates', 'images'),
);
} catch (err) {
// Folder doesn't exist.
}
const phpDataObject = await execPHPFile(path.join(phpGeneratorPath, 'build.php'), 'jsonToPhp', {
json: await readJSONFile(path.join(projectPath, 'data', 'default.json'), "utf8"),
});
// await createFiles(Object.assign({}, data, {defaultData: phpDataObject}), [{
// from: `templates/helpers/Template_Defaults.php`,
// to: `helpers/${data.blockClassModel}_Defaults.php`,
// }], {
// pathDist: distPath,
// generatorsPath: path.join(__dirname),
// });
if (isElementor) {
await createFiles(data, [{
from: `templates/helpers/Template_Elementor_Widget.php`,
to: `helpers/${data.blockClassModel}_Elementor_Widget.php`,
@@ -93,25 +82,7 @@ export async function buildWordPress(blockName, args = {}) {
pathDist: distPath,
generatorsPath: path.join(__dirname)
});
}
if (isBlock) {
await createFiles(data, [{
from: `templates/Template_Component.php`,
to: `${data.blockClassModel}_Component.php`,
}], {
pathDist: distPath,
generatorsPath: path.join(__dirname)
});
// await createFiles(data, [{
// from: `templates/helpers/Template_API.php`,
// to: `helpers/${data.blockClassModel}_API.php`,
// }], {
// pathDist: distPath,
// generatorsPath: path.join(__dirname)
// });
} else {
await createFiles(data, [{
from: `templates/Template_Basic_Component.php`,
to: `${data.blockClassModel}_Component.php`,
@@ -119,7 +90,6 @@ export async function buildWordPress(blockName, args = {}) {
pathDist: distPath,
generatorsPath: path.join(__dirname)
});
}
}
export function execCommand(cmd = '') {
+22
View File
@@ -47,4 +47,26 @@ export default [{
commonjs(),
!devMode && terser()
]
}, {
input: 'layouts/scripts/sync.jsx',
output: {
file: 'layouts/scripts/dist/sync.min.js',
sourcemap: devMode
},
plugins: [
nodeResolve({
extensions: [".js"],
}),
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
preventAssignment: true,
}),
babel({
compact: false,
babelHelpers: 'bundled',
presets: ["@babel/preset-react"],
}),
commonjs(),
!devMode && terser()
]
}];
+203 -42
View File
@@ -19,11 +19,27 @@ import open from "open";
import {sanitizeUrl} from "@braintree/sanitize-url";
import sanitizeHtml from 'sanitize-html';
import {escape} from "lodash-es";
import {getBlockData, getBlockConfigs, getConfigs, readJSONFile, zipProject} from "./helpers.js";
import {
getBlockConfigs,
getBlockData,
getBlockName,
getConfigs,
getImagesList,
isFileEmpty,
readJSONFile,
removeCommentsFromCss,
removeCommentsFromJs,
replaceNames,
syncFilesWithCloud,
uploadFile,
verifyVersion,
zipProject
} from "./helpers.js";
import PluginError from 'plugin-error';
import {Server} from "socket.io";
import {createServer} from 'http';
import {authtoken, connect} from "ngrok";
// import {authtoken, connect} from "ngrok";
import bodyParser from "body-parser";
/**
* Constants
@@ -33,7 +49,8 @@ const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs();
const blocksRegistry = isDev ? 'http://localhost:3020' : PRODUCTION_REGISTRY_URL;
const DevToolToken = 'D9lgz0TvzXCnp0xnwVBL109DaAR6Puk6F7YewDhgmP8='; // Temporary token for development purposes.
const dataFiles = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
const dataFiles = await getDataFiles(projectPath);
const DEFAULT_VIEW_LAYOUT = 'alignfull';
/**
* State
@@ -43,12 +60,21 @@ let port = 3000; // This variable is used in `*.hbs` and it will be updated once
let previewFrameUrl = `/`; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
let shareUrl = '';
const sessions = [];
let ignoreVersionSync = false;
let bs;
/**
* Init server
*/
const app = express();
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}))
// parse application/json
app.use(bodyParser.json())
const httpServer = createServer(app);
initSessionsServer(httpServer);
@@ -63,8 +89,6 @@ const hbs = create({
}, esc_html(html) {
// TODO: Check if we can remove this helper.
return html;
}, safe_html(html) {
return sanitizeHtml(html);
}
}
});
@@ -77,58 +101,84 @@ app.set('views', path.join(modulesPath, 'layouts'));
// Routes
//
app.get('/', (req, res) => {
app.get('/', async (req, res, next) => {
const data = getBlockConfigs({modulesPath, dataFiles});
if (data.error && data.errorMessage) {
return res.send(data.errorMessage);
}
const baseView = config.has('baseView') ? config.get('baseView') : 'container';
const baseView = config.has('baseView') ? config.get('baseView') : DEFAULT_VIEW_LAYOUT;
const baseViewUrl = `view/${baseView}`;
data.helpers = {
include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath),
}
try {
const verifiedVersion = await verifyVersion(projectPath, blocksRegistry);
if (!verifiedVersion && !ignoreVersionSync) {
return res.render('sync', data);
}
} catch (err) {
const errorMessage = "Can't verify block version.";
console.log(errorMessage, err);
return next(new Error(errorMessage));
}
data.baseView = baseView;
data.port = `/${baseViewUrl}`;
data.previewFrameUrl = `${previewFrameUrl}/${baseViewUrl}`;
// data.previewFrameUrl = `/${baseViewUrl}`;
data.shareUrl = shareUrl;
if (req.headers.referer) {
// NGROK, public URL
data.shareUrl = undefined; // Link already shared.
data.previewFrameUrl = `/${baseViewUrl}`;
data.publicUrl = true;
}
// TODO: Need to review this logic, conflicts with the browsersync work after "/sync" action.
// if (req.headers.referer) {
// // NGROK, public URL
// data.shareUrl = undefined; // Link already shared.
// data.previewFrameUrl = `/${baseViewUrl}`;
// data.publicUrl = true;
// }
res.render('index', data);
});
app.get('/view/:baseView', (req, res) => {
const data = {config: getBlockConfigs({modulesPath, dataFiles})};
const data = getDataOfFrame(!!req.query.iframe);
if (data.error && data.errorMessage) {
return res.send(data.errorMessage);
}
data.helpers = {
include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath),
}
if (!!req.query.iframe) {
data.iframeMode = true;
}
const baseView = req.params.baseView ?? 'container';
const baseView = req.params.baseView ?? DEFAULT_VIEW_LAYOUT;
res.render(baseView, data)
});
app.get('/publish', async (req, res) => {
const data = await readJSONFile(path.join(projectPath, `block.json`));
// Trigger build on the registry server only if the type of the unit is `foundation` or `component`.
const uploadStaticFiles = ['foundation', 'component'].includes(data.type);
// Prepare list of static files for the registry server.
if (uploadStaticFiles) {
data.static_files = {
css: getBlockName(data.name).name + '.min.css',
js: getBlockName(data.name).name + '.min.js',
images: await getImagesList(path.join(projectPath, 'src', 'images')),
}
if (await isFileEmpty(path.join(projectPath, `src/scripts`, data.static_files.js), true)) {
delete data.static_files.js;
}
if (!data.static_files.images.length) {
delete data.static_files.images;
}
}
let responseData = {
uploadUrl: undefined
uploadBundleUrl: undefined,
staticFilesUrls: {}
};
try {
@@ -149,35 +199,87 @@ app.get('/publish', async (req, res) => {
return;
}
if (responseData.uploadUrl) {
// Start files uploading process.
try {
if (responseData.uploadBundleUrl) {
await zipProject(path.join(projectPath, 'src'), path.join(projectPath, 'dist.zip'));
const body = await fs.readFile(path.join(projectPath, 'dist.zip'));
const response = await fetch(`${responseData.uploadUrl}`, {
method: 'PUT',
body,
headers: {'Content-Type': 'application/zip'}
});
await uploadFile(path.join(projectPath, 'dist.zip'), responseData.uploadBundleUrl); // Bundle
}
if (response.status !== 200) {
res.json({success: false, message: "Can't upload the archive, permissions error."});
if (uploadStaticFiles) {
if (responseData.staticFilesUrls.css) {
await uploadFile(
path.join(projectPath, 'src/styles', data.static_files.css),
responseData.staticFilesUrls.css.uploadUrl,
(content) => {
if (responseData.staticFilesUrls.images) {
content = replaceNames(content, data.static_files.images, responseData.staticFilesUrls.images);
}
removeCommentsFromCss(content)
return content;
}); // CSS
}
if (responseData.staticFilesUrls.js) {
await uploadFile(
path.join(projectPath, 'src/scripts', data.static_files.js),
responseData.staticFilesUrls.js.uploadUrl,
(data) => removeCommentsFromJs(data)
); // JS
}
if (responseData.staticFilesUrls.images) {
for (let i = 0; i < data.static_files.images.length; i++) {
await uploadFile(
path.join(projectPath, 'src/images', data.static_files.images[i]),
responseData.staticFilesUrls.images[i].uploadUrl,
); // Images
}
}
}
} catch (err) {
// TODO: Need to update the registry server.
await fs.unlink(path.join(projectPath, 'dist.zip'));
await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
res.json({success: false, message: err.message});
return;
}
await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
// Trigger project's global files build on the registry server.
if (uploadStaticFiles) {
try {
await triggerGlobalProjectFilesBuild(getBlockName(data.name).project);
} catch (err) {
res.json({success: false, message: 'Something wrong with Project Builder.'});
return;
}
}
try {
await syncFilesWithCloud(path.join(projectPath, 'block.json'), bs);
} catch (err) {
res.json({success: false, message: err});
return;
}
res.json({success: true});
await fs.unlink(path.join(projectPath, 'dist.zip'));
});
app.get('/data', async (req, res) => {
let jsonDataFileName = req.query.name ? req.query.name : 'default';
const dataFiles = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
const dataFiles = await getDataFiles(projectPath);
const data = await getBlockData(jsonDataFileName, {projectPath});
const designPreviewFiles = getListOfDesignPreviewFiles(jsonDataFileName, await fs.readdir(path.join(projectPath, 'design', 'preview')));
let designPreviewFiles = [];
try {
designPreviewFiles = getListOfDesignPreviewFiles(jsonDataFileName, await fs.readdir(path.join(projectPath, 'design', 'preview')));
} catch (err) {
console.log('Preview Design doesn\'t exist');
}
return res.json({
dataOptions: dataFiles,
@@ -186,6 +288,22 @@ app.get('/data', async (req, res) => {
});
});
app.post('/sync', async (req, res) => {
if (req.body['ignore']) {
ignoreVersionSync = true;
res.json({status: 200, message: 'Version upgrade is ignored.'});
return;
}
try {
await syncFilesWithCloud(path.join(projectPath, `block.json`), bs, true);
} catch (err) {
res.status(500).json({status: 500, message: err});
}
res.json({status: 200, message: 'Successfully synced!'});
});
// Errors handler
app.use(handleSyntaxErrors);
@@ -230,6 +348,23 @@ function getListOfDesignPreviewFiles(jsonDataFileName, previewFiles) {
});
}
function getDataOfFrame(isIframe = false) {
const data = {config: getBlockConfigs({modulesPath, dataFiles})};
if (data.error && data.errorMessage) {
return data;
}
data.helpers = {
include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath),
}
if (isIframe) {
data.iframeMode = true;
}
return data;
}
function startBrowserSync() {
return new Promise((resolve, reject) => {
const listener = httpServer.listen(0, async () => {
@@ -237,7 +372,8 @@ function startBrowserSync() {
console.log(`The web server has started on port ${PORT}`);
const bs = browserSync.create();
// BS is global variable.
bs = browserSync.create();
const files = getJSBundleFiles();
gulp.watch(files, {delay: 400}, gulp.series(['build-script-files', function (cb) {
@@ -431,11 +567,36 @@ async function getShareableUrl() {
let url;
try {
await authtoken(data.ngrokApiToken);
url = await connect(port);
// await authtoken(data.ngrokApiToken);
// url = await connect(port);
} catch (e) {
console.log('Error:', `Can't connect to ngrok, probably wrong token.`);
}
return url;
}
async function triggerGlobalProjectFilesBuild(project) {
const response = await fetch(`${blocksRegistry}/project-files`, {
method: 'POST',
body: JSON.stringify({project}),
headers: {'Content-Type': 'application/json'}
});
return response.json();
}
async function getDataFiles(projectPath) {
const dataFiles = [];
try {
await fs.access(path.join(projectPath, 'data'));
const files = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
dataFiles.push(...files);
} catch (e) {
console.log('Warning: data folder not found.');
}
return dataFiles;
}