Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4192d16b7a | |||
| 12abd35cfa | |||
| 073ed52932 | |||
| 8f2605be9d | |||
| 9e7860542b | |||
| f48b2a3274 | |||
| b76f83d4fa | |||
| 977ec0003c | |||
| 0e91c42e89 | |||
| 0a4fd3608f | |||
| 11152dda87 | |||
| 246c9ddd35 | |||
| 164ef7433a | |||
| a6decb9cfb | |||
| 9c85a89a8f | |||
| 58ed3de9dc | |||
| 3de4a4abb6 | |||
| 256450226b | |||
| 51db3da072 | |||
| 05c5697bc2 | |||
| 01e4160040 | |||
| 81f5166043 | |||
| a1929fe33c | |||
| 840c002834 | |||
| f4474fb7e4 | |||
| 8ca6cb0365 | |||
| e2553358be | |||
| 34dbff5701 | |||
| c408f2f5b3 | |||
| 15dbe26220 | |||
| 9818067013 | |||
| 996d222436 | |||
| e964892e65 | |||
| 6d1ab34b2b | |||
| 18d89a9a19 | |||
| 5c7ea9df3c | |||
| 010154797a | |||
| 98d0720bc0 | |||
| 378038b244 | |||
| 090978e8f8 | |||
| aae474901b | |||
| a4193d63e1 | |||
| 9f5c157e54 | |||
| b399d2fc89 | |||
| c899f8c229 | |||
| 80464a61d7 | |||
| 3a78aee088 | |||
| 0f60a0ac30 | |||
| 7dcaa80d71 | |||
| 0a8cdf1504 | |||
| 1718157b4e | |||
| df7866b8b5 | |||
| 50d743140b | |||
| f86afc8394 | |||
| 269fbbc401 | |||
| f236674c1e | |||
| e9827fdd88 | |||
| b441ac613d | |||
| 394d5a42d9 | |||
| 6885db162e | |||
| 1a19d63192 | |||
| 3c163b8e76 | |||
| c627c873f9 | |||
| ec61e01950 | |||
| 682299445c | |||
| ddbc713603 | |||
| 4abdd29709 | |||
| a0def8467b | |||
| bd5cafa549 | |||
| dd860ec473 | |||
| c1340f2035 | |||
| b43a4d6b4f | |||
| 1a98a60fac | |||
| de0b6740d1 | |||
| f9c0852ede | |||
| cdbb4d3064 | |||
| 8d7ce53e46 | |||
| e42643f182 | |||
| 05b5ccb2b5 | |||
| 54523ed903 | |||
| f611185a47 | |||
| 7024c2bfcb | |||
| b882c9a1c3 | |||
| 1dc8d192f7 | |||
| d8c3d4c54b | |||
| 6626ecff2a | |||
| 25862c4b1c | |||
| a61510f136 | |||
| 0d3ae8f03e | |||
| b58ef27f1e | |||
| 161e34e8ee | |||
| 8eb7bea2e8 | |||
| d9543bb22c | |||
| 02abf4652e | |||
| 1ed038ff02 | |||
| 0bb705ccdc | |||
| 9d25bdcb88 | |||
| 4d1d2337bf | |||
| 00866a8115 | |||
| 1d733fb952 | |||
| 295c6f82af | |||
| f0af682a43 | |||
| ad35540111 | |||
| 5cb6343184 | |||
| 502326fd8d | |||
| 38a2d60f2c | |||
| 15c5286905 | |||
| f3e5b92318 | |||
| c42c9fecba | |||
| 6ebd37190a | |||
| 37ecce6290 | |||
| e0ae4d3d69 | |||
| 70d4630971 | |||
| a2f8c7ebc1 | |||
| 1ae643b306 | |||
| 52109f5010 | |||
| e0fb681d3b | |||
| 840f73d739 | |||
| 702cd6e226 | |||
| eabe411b7a | |||
| 7bf571e0fb | |||
| 73a10d9d55 | |||
| 527416c63d | |||
| ac70e7eb9a | |||
| 05660eb4e4 | |||
| 499c5b8163 | |||
| 16f65254da |
@@ -18,6 +18,10 @@ indent_size = 2
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.php]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
FROM node:19.4.0-slim as node
|
||||||
|
|
||||||
|
WORKDIR /app/
|
||||||
|
|
||||||
|
COPY ./package.json .
|
||||||
|
COPY ./package-lock.json .
|
||||||
|
COPY ./inc ./inc
|
||||||
|
COPY ./layouts ./layouts
|
||||||
|
COPY ./platforms ./platforms
|
||||||
|
COPY ./routes ./routes
|
||||||
|
COPY ./env.js .
|
||||||
|
COPY ./helpers.js .
|
||||||
|
COPY ./rollup.config.js .
|
||||||
|
COPY ./server.js .
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
EXPOSE 3010
|
||||||
|
CMD npm run view-mode
|
||||||
@@ -1,74 +1,57 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
// For development purposes - run `npm run build-platform`.
|
||||||
|
|
||||||
import {exec} from 'child_process';
|
|
||||||
import config from 'config';
|
import config from 'config';
|
||||||
import Generator from "yeoman-generator";
|
import prompts from "prompts";
|
||||||
import yeoman from 'yeoman-environment';
|
import {buildExportFiles, getConfigs} from "./helpers.js";
|
||||||
import {buildHubspot} from "./platforms/hubspot/hubspot-adapter.js";
|
|
||||||
import {getConfigs} from "./helpers.js";
|
|
||||||
import {buildWordPress} from "./platforms/wordpress/wordpress-adapter.js";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
const {modulesPath, projectPath} = getConfigs();
|
const {isDev, developmentBlockName} = getConfigs();
|
||||||
|
const blockName = !isDev && config.has('blockName') ? config.get('blockName') : developmentBlockName;
|
||||||
|
|
||||||
const blockName = config.has('blockName') ? config.get('blockName') : 'development';
|
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'
|
||||||
|
}, {
|
||||||
|
name: 'hubspot-email',
|
||||||
|
title: 'Hubspot Email'
|
||||||
|
}, {
|
||||||
|
name: 'javascript',
|
||||||
|
title: 'JavaScript'
|
||||||
|
}, {
|
||||||
|
name: 'php',
|
||||||
|
title: 'PHP'
|
||||||
|
}];
|
||||||
|
|
||||||
class buildGenerator extends Generator {
|
const data = await getExportData();
|
||||||
async prompting() {
|
const selectedPlatform = PLATFORM_OPTIONS[data['platform']];
|
||||||
this.data = await this.prompt([
|
await buildExportFiles(blockName, selectedPlatform);
|
||||||
{
|
|
||||||
type: "list",
|
|
||||||
name: "platform",
|
|
||||||
message: "Choose Platform",
|
|
||||||
choices: ['WordPress', 'WordPress Block', 'Hubspot', 'Hubspot Email', 'JavaScript', 'PHP'],
|
|
||||||
default: 'WordPress'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
writing() {
|
console.log('--------------------\nDone!');
|
||||||
new Promise((resolve => {
|
|
||||||
if (['WordPress', 'PHP'].includes(this.data.platform)) {
|
|
||||||
const backPath = modulesPath ? modulesPath.split('/').map(() => '..').join('/') : '';
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
//
|
||||||
const phpGeneratorPath = path.join(modulesPath, 'platforms', 'php');
|
// Functions
|
||||||
exec(`cd ${phpGeneratorPath} && composer install && php build.php '${blockName}' '${backPath}' '${projectPath}'`, function (error, stdout) {
|
//
|
||||||
if (error) {
|
|
||||||
console.log('Error:', error)
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(stdout);
|
function getExportData() {
|
||||||
resolve();
|
return prompts([
|
||||||
});
|
{
|
||||||
}).then(() => {
|
type: "select",
|
||||||
if (this.data.platform === 'WordPress') {
|
name: "platform",
|
||||||
return buildWordPress();
|
message: "Choose Platform",
|
||||||
}
|
choices: PLATFORM_OPTIONS.map(item => item.title),
|
||||||
|
default: 'WordPress'
|
||||||
if (this.data.platform === 'WordPress Block') {
|
}
|
||||||
return buildWordPress(true);
|
]);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (this.data.platform === 'Hubspot Email') {
|
|
||||||
buildHubspot(blockName)
|
|
||||||
.then(() => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
} else if (this.data.platform === 'Hubspot') {
|
|
||||||
console.log('"Hubspot" Coming soon...');
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.then(() => {
|
|
||||||
console.log('--------------------\nDone!');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const build = new buildGenerator([], {env: yeoman.createEnv()}, {});
|
|
||||||
build.run().then(() => null);
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import {getConfigs} from "./helpers.js";
|
||||||
|
|
||||||
|
const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs();
|
||||||
|
|
||||||
|
console.log('----------------------------------\n')
|
||||||
|
console.log('Local Details', process.versions, '\n');
|
||||||
|
console.log('Configs', {
|
||||||
|
isDev,
|
||||||
|
modulesPath,
|
||||||
|
projectPath,
|
||||||
|
developmentBlockName
|
||||||
|
});
|
||||||
|
console.log('\n----------------------------------\n')
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since this file overwrites environment variables for `config` lib,
|
||||||
|
* it's important to "import" this file before all scripts in entry point file,
|
||||||
|
* especially before `config` module import.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export constant variables
|
||||||
|
*/
|
||||||
|
export const PRODUCTION_REGISTRY_URL = 'https://blocks-registery.axe-web.com';
|
||||||
|
export const IS_DEV = process.env.NODE_ENV === 'development';
|
||||||
|
export const BLOCK_NAME = process.env.BLOCK_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite env variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
process.env.NODE_CONFIG_DIR = path.join(getProjectPath(), 'config');
|
||||||
|
|
||||||
|
|
||||||
|
export function getModulePath() {
|
||||||
|
let modulePath = 'node_modules/block-dev-tool';
|
||||||
|
|
||||||
|
if (typeof process.env.MODULE_PATH !== 'undefined') {
|
||||||
|
modulePath = process.env.MODULE_PATH;
|
||||||
|
} else if (process.env.BLOCK_NAME) {
|
||||||
|
modulePath = 'node_modules/@axe-web/block-dev-tool';
|
||||||
|
}
|
||||||
|
|
||||||
|
return modulePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectPath() {
|
||||||
|
let projectPath = '';
|
||||||
|
|
||||||
|
if (typeof process.env.PROJECT_PATH !== 'undefined') {
|
||||||
|
projectPath = path.join(process.env.PROJECT_PATH ?? '', process.env.BLOCK_NAME ?? '')
|
||||||
|
} else if (process.env.BLOCK_NAME) {
|
||||||
|
projectPath = path.join('blocks', process.env.BLOCK_NAME ?? '')
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectPath;
|
||||||
|
}
|
||||||
+313
-30
@@ -1,30 +1,38 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import {BLOCK_NAME, getModulePath, getProjectPath, IS_DEV} from "./env.js";
|
||||||
import config from 'config';
|
import config from 'config';
|
||||||
import {fileURLToPath} from 'url';
|
import {fileURLToPath} from 'url';
|
||||||
import memFs from 'mem-fs';
|
import memFs from 'mem-fs';
|
||||||
import editor from 'mem-fs-editor';
|
import editor from 'mem-fs-editor';
|
||||||
|
import fsExtra from "fs-extra";
|
||||||
|
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";
|
||||||
|
import {REGISTRY_URL} from "@axe-web/create-block/env.js";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
export function getConfigs() {
|
export function getConfigs() {
|
||||||
const isDev = process.env.NODE_ENV === 'development'; // Check README file in case you get "missing files" error.
|
|
||||||
const developmentBlockName = process.env.BLOCK_NAME;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isDev,
|
isDev: IS_DEV,
|
||||||
developmentBlockName,
|
developmentBlockName: BLOCK_NAME,
|
||||||
modulesPath: isDev ? '' : 'node_modules/block-dev-tool',
|
modulesPath: getModulePath(),
|
||||||
projectPath: isDev ? path.join('blocks', developmentBlockName) : '',
|
projectPath: getProjectPath(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
import fsExtra from "fs-extra";
|
|
||||||
|
|
||||||
export async function readJSONFile(jsonFile) {
|
export async function readJSONFile(jsonFile) {
|
||||||
let data = {};
|
let data = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await fs.access(jsonFile, constants.F_OK | constants.R_OK);
|
||||||
data = await fsExtra.readJson(jsonFile);
|
data = await fsExtra.readJson(jsonFile);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
@@ -42,37 +50,47 @@ function getErrorHtml(message = '', errorMessage = '') {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBlockConfigs(jsonFileName = 'default',
|
export async function getBlockData(jsonFileName = 'default', {projectPath} = {jsonFileName: 'default'}) {
|
||||||
{includeConfigs, projectPath, modulesPath, dataFiles} = {}) {
|
const filePath = path.join(projectPath, 'data', `${jsonFileName}.json`);
|
||||||
let data = await readJSONFile(path.join(projectPath, 'data', `${jsonFileName}.json`));
|
const data = await readJSONFile(filePath);
|
||||||
if (data.error) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeConfigs) {
|
if (data.error) {
|
||||||
Object.assign(data, {
|
console.log(filePath, data.errorMessage.replace(/<[^>]*>?/gm, ''));
|
||||||
config: Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
|
return {};
|
||||||
{
|
|
||||||
projectDir: modulesPath, activeDataFile: jsonFileName, dataFiles: dataFiles.map((name) => {
|
|
||||||
return {
|
|
||||||
name, active: jsonFileName === name,
|
|
||||||
};
|
|
||||||
}), remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBlockName(name = '') {
|
export async function getBlockConfigs(args) {
|
||||||
if (name.startsWith('@')) {
|
const config = await readJSONFile(path.join('blocks', '@' + args.project, args.name, 'config', 'default.json'));
|
||||||
name = name.substring(1);
|
|
||||||
|
const updatedConfig = Object.assign({}, config, // The entire config object.
|
||||||
|
{
|
||||||
|
projectDir: '', dataFiles: args.dataFiles.map((name) => {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
}), 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 = '') {
|
||||||
|
// Remove all @ from the beginning of the string.
|
||||||
|
name = name.replace(/^@/, '');
|
||||||
|
|
||||||
const arr = name.split('/');
|
const arr = name.split('/');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
blockName: `@${arr[0]}/${arr[1]}`,
|
||||||
project: arr[0],
|
project: arr[0],
|
||||||
name: arr[1],
|
name: arr[1],
|
||||||
};
|
};
|
||||||
@@ -100,10 +118,275 @@ export function capitalize(str) {
|
|||||||
|
|
||||||
return str
|
return str
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(/[ -_]/g)
|
.split(/[-_ ]/g)
|
||||||
.filter((word) => !!word)
|
.filter((word) => !!word)
|
||||||
.map((word) => {
|
.map((word) => {
|
||||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
})
|
})
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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
|
||||||
|
archive.on('error', function (err) {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
// pipe archive data to the file
|
||||||
|
archive.pipe(output);
|
||||||
|
|
||||||
|
// append files from a subdirectory, putting its contents at the root of 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildExportFiles(blockName, platform) {
|
||||||
|
if (platform.name.startsWith('wordpress')) {
|
||||||
|
await buildWordPress(blockName, {platform: platform.name});
|
||||||
|
} else if (platform.name === 'hubspot-email') {
|
||||||
|
await buildHubspotEmail(blockName)
|
||||||
|
} else if (platform.name === 'hubspot') {
|
||||||
|
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.replace(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. [${blockName}]`
|
||||||
|
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(blockName, projectPath, blocksRegistry) {
|
||||||
|
const block = await getBlockFromCloud(blockName, blocksRegistry);
|
||||||
|
if (!block) {
|
||||||
|
throw new Error(`Block not found. [${blockName}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockJson = await readJSONFile(path.join(projectPath, `block.json`));
|
||||||
|
if (blockJson.error) {
|
||||||
|
return false; // Block doesn't exist locally.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This block is managed on cloud.
|
||||||
|
* Let's detect the latest version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return block.version === blockJson.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncFilesWithCloud(blockName, bs, sourceFiles = false) {
|
||||||
|
const block = await getBlockFromCloud(blockName, REGISTRY_URL);
|
||||||
|
|
||||||
|
let fileExists = false;
|
||||||
|
try {
|
||||||
|
await readJSONFile(path.join('blocks', '@' + block.project, block.name, 'block.json'));
|
||||||
|
fileExists = true;
|
||||||
|
} catch (e) {
|
||||||
|
// Dir doesn't exist.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Pause watcher of the block?
|
||||||
|
blockName = `@${block.project}/${block.name}`;
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const createBlockModulePath = `./node_modules/@axe-web/create-block`;
|
||||||
|
const action = sourceFiles ? fileExists ? 'sync --source' : 'pull' : 'sync';
|
||||||
|
|
||||||
|
exec(`BLOCK_NAME=${blockName} node ${createBlockModulePath}/create-block.js ${action}`, (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) {
|
||||||
|
const message = err || stderr;
|
||||||
|
console.error('Error:', message);
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(stdout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Release watcher of the block?
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBlockVariations(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.', projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareListOfDataFiles(dataFiles) {
|
||||||
|
return dataFiles
|
||||||
|
.filter((fileName) => fileName.split('.').pop() === 'json')
|
||||||
|
.map((fileName) => {
|
||||||
|
const splitName = fileName.split('.');
|
||||||
|
splitName.pop();
|
||||||
|
return splitName.join('');
|
||||||
|
})
|
||||||
|
.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handlebarLayoutsPath() {
|
||||||
|
return path.join(...arguments)
|
||||||
|
.replace(/\\/g, '/'); // Windows path issue. Fix all "\" for Handlebars.
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getListOfDesignPreviewFiles(jsonDataFileName, previewFiles, block) {
|
||||||
|
return previewFiles
|
||||||
|
.filter(fileName => {
|
||||||
|
return fileName.startsWith(jsonDataFileName + '.');
|
||||||
|
})
|
||||||
|
.map(fileName => {
|
||||||
|
const fileData = fileName.split('.');
|
||||||
|
const fileFormat = fileData.pop();
|
||||||
|
const previewSize = fileData.pop();
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataSource: jsonDataFileName,
|
||||||
|
widthDimension: Number.parseInt(previewSize, 10),
|
||||||
|
url: `/design/${block.project}/${block.name}/preview/${fileName}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import gulp from "gulp";
|
||||||
|
import path from "path";
|
||||||
|
import sourcemaps from "gulp-sourcemaps";
|
||||||
|
import babel from "gulp-babel";
|
||||||
|
import PluginError from "plugin-error";
|
||||||
|
import rename from "gulp-rename";
|
||||||
|
import uglify from "gulp-uglify";
|
||||||
|
import dartSass from 'sass';
|
||||||
|
import gulpSass from "gulp-sass";
|
||||||
|
import {getBlockName} from "../helpers.js";
|
||||||
|
|
||||||
|
const sass = gulpSass(dartSass);
|
||||||
|
|
||||||
|
export function setupWatcher(viewSync) {
|
||||||
|
|
||||||
|
const watchSCSS = gulp.watch(['blocks/**/*.scss'], {delay: 400});
|
||||||
|
watchSCSS.on('change', async function (filepath, stats) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
buildStyleFiles(path.join('blocks', '@' + block.project, block.name), () => {
|
||||||
|
viewSync.syncTemplate(block.blockName, 'styleUpdate');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchJS = gulp.watch(['blocks/**/*.js', '!blocks/**/*.min.js'], {delay: 400});
|
||||||
|
watchJS.on('change', async function (filepath) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
buildScriptFiles(path.join('blocks', '@' + block.project, block.name), () => {
|
||||||
|
viewSync.syncTemplate(block.blockName, 'scriptUpdate');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchHBS = gulp.watch(['blocks/**/*.hbs'], {delay: 400});
|
||||||
|
watchHBS.on('change', async function (filepath) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
await viewSync.syncTemplate(block.blockName);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAssetFiles(projectPath) {
|
||||||
|
// Register tasks.
|
||||||
|
gulp.task('build-script-files', (done) => buildScriptFiles(projectPath, done));
|
||||||
|
gulp.task('build-styling-files', (done) => buildStyleFiles(projectPath, done));
|
||||||
|
|
||||||
|
// Run first build.
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
gulp.series('build-script-files', 'build-styling-files', function () {
|
||||||
|
resolve();
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(errorMessage) {
|
||||||
|
console.log(errorMessage);
|
||||||
|
|
||||||
|
// TODO: Send this message to browser.
|
||||||
|
// So the developer can understand there is an error.
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildScriptFiles(projectPath = '', done) {
|
||||||
|
const files = getJSBundleFiles(projectPath);
|
||||||
|
const stream = gulp.src(files, {base: path.posix.join(projectPath, 'src')})
|
||||||
|
.pipe(sourcemaps.init({}))
|
||||||
|
.pipe(babel()).on('error', function (error) {
|
||||||
|
showError(new PluginError('JavaScript', error).toString());
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.pipe(gulp.src(path.join(projectPath, 'vendor/*.js')))
|
||||||
|
// .pipe(gulp.dest('src/'))
|
||||||
|
.pipe(rename({extname: '.min.js'}))
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src')));
|
||||||
|
|
||||||
|
stream.on('end', done);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSBundleFiles(projectPath = '') {
|
||||||
|
return [path.posix.join(projectPath, "src/**/*.js"), path.posix.join(projectPath, "src/**/*.mjs"), "!" + path.posix.join(projectPath, "src/**/*.min.js")];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStyleFiles(projectPath = '', done) {
|
||||||
|
const stream = gulp.src(path.join(projectPath, 'src/**/*.scss'), {base: path.posix.join(projectPath, 'src')})
|
||||||
|
.pipe(sourcemaps.init({}))
|
||||||
|
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', function (error) {
|
||||||
|
showError(new PluginError('SCSS', error.messageFormatted).toString());
|
||||||
|
// sass.logError(error);
|
||||||
|
done();
|
||||||
|
}))
|
||||||
|
// .pipe(gulp.dest('src/'))
|
||||||
|
.pipe(rename({extname: '.min.css'}))
|
||||||
|
.pipe(sourcemaps.write('.', {}))
|
||||||
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src')));
|
||||||
|
|
||||||
|
stream.on('end', done);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export function setHeaders(req, res, next) {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
next();
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import express from "express";
|
||||||
|
import bodyParser from "body-parser";
|
||||||
|
import {createServer} from "http";
|
||||||
|
import {create} from "express-handlebars";
|
||||||
|
import {escape} from "lodash-es";
|
||||||
|
import {sanitizeUrl} from "@braintree/sanitize-url";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// parse application/x-www-form-urlencoded
|
||||||
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
|
|
||||||
|
// parse application/json
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
const hbs = getHandlebarsViewEngine();
|
||||||
|
|
||||||
|
app.engine('.hbs', hbs.engine);
|
||||||
|
app.set('view engine', '.hbs');
|
||||||
|
|
||||||
|
const modulesPath = '.';
|
||||||
|
app.set('views', path.join(modulesPath, 'layouts'));
|
||||||
|
|
||||||
|
// Static Files of blockDevTool.
|
||||||
|
app.use(express.static(path.join(modulesPath, 'layouts')));
|
||||||
|
|
||||||
|
return {
|
||||||
|
expressApp: app,
|
||||||
|
httpServer: createServer(app),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHandlebarsViewEngine() {
|
||||||
|
return create({
|
||||||
|
extname: '.hbs', defaultLayout: false, partialsDir: ['.'], helpers: handlebarsHelpers()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlebarsHelpers() {
|
||||||
|
return {
|
||||||
|
esc_attr(attr) {
|
||||||
|
return escape(attr);
|
||||||
|
},
|
||||||
|
esc_url(url) {
|
||||||
|
return sanitizeUrl(url);
|
||||||
|
},
|
||||||
|
esc_html(html) {
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {Server} from "socket.io";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import {getBlockName, handlebarLayoutsPath} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export class ViewSync {
|
||||||
|
constructor(httpServer) {
|
||||||
|
this.sessions = {};
|
||||||
|
this.init(httpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSession(session, blockName) {
|
||||||
|
if (!this.sessions[blockName]) {
|
||||||
|
this.sessions[blockName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessions[blockName].push(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(httpServer) {
|
||||||
|
const io = new Server(httpServer);
|
||||||
|
io.on('connection', async (socket) => {
|
||||||
|
const blockName = socket.handshake.query.block;
|
||||||
|
|
||||||
|
if (!blockName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addSession(socket, blockName);
|
||||||
|
await this.syncTemplate(blockName);
|
||||||
|
});
|
||||||
|
|
||||||
|
// return httpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncTemplate(blockName, updateType = 'templateUpdate') {
|
||||||
|
const block = getBlockName(blockName);
|
||||||
|
|
||||||
|
const projectPath = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const hbsTemplate = await fs.readFile(handlebarLayoutsPath(projectPath, 'src', `${block.name}.template.hbs`), 'utf8');
|
||||||
|
|
||||||
|
if (!this.sessions[blockName]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessions[blockName].forEach(session => {
|
||||||
|
session.emit(updateType, {block: blockName, template: hbsTemplate});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/head") }}
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
<body>
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
<main>
|
<div id="hbs-container"></div>
|
||||||
{{> (include_block_template) }}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/scripts") }}
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/head") }}
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
<body>
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
<main>
|
<div class="container">
|
||||||
<div class="container">
|
<div id="hbs-container"></div>
|
||||||
{{> (include_block_template) }}
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/scripts") }}
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/head") }}
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|||||||
+7
-2
@@ -1,3 +1,4 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
@@ -7,18 +8,22 @@
|
|||||||
<title>Block Development Tool</title>
|
<title>Block Development Tool</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="{{#if viewMode }}view-mode{{/if}}">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/toolbar") }}
|
{{> (include_partial "layouts/partials/toolbar") }}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.devTool = {
|
window.devTool = {
|
||||||
|
blockName: '{{ blockName }}',
|
||||||
previewFrameUrl: '{{ previewFrameUrl }}',
|
previewFrameUrl: '{{ previewFrameUrl }}',
|
||||||
|
{{#if publicUrl}}
|
||||||
|
publicUrl: true,
|
||||||
|
{{/if}}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
<iframe id="preview_frame" src="{{ previewFrameUrl }}" class="breakpoint"></iframe>
|
<iframe id="preview_frame" src="{{ previewFrameUrl }}?iframe=true&block={{blockName}}" class="breakpoint"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/scripts/dist/index.min.js"></script>
|
<script src="/scripts/dist/index.min.js"></script>
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<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 }}
|
{{#if config.cssUrl }}
|
||||||
{{#each config.cssUrl }}
|
{{#each config.cssUrl }}
|
||||||
<link rel="stylesheet" href="{{ this }}">
|
<link rel="stylesheet" href="{{ this }}">
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}<link rel="stylesheet" href="/styles/page--view.css">{{# if config.blockName}}
|
{{/if}}<link rel="stylesheet" href="/styles/page--view.css">{{# if config.blockName}}
|
||||||
<link rel="stylesheet" href="/styles/{{ config.blockName }}.min.css">{{/if}}
|
<link rel="stylesheet" id="block-stylesheet" href="{{staticFilesPath}}/styles/{{ config.blockName }}.min.css?v=1">{{/if}}
|
||||||
<link rel="stylesheet" href="https://unpkg.com/swiper@8/swiper-bundle.min.css"/>
|
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
<script src="/scripts/frame-index.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<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://code.jquery.com/jquery-3.4.1.min.js"></script>
|
||||||
<script src="https://unpkg.com/swiper@8/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 config.jsUrl }}<script src="{{ this }}"></script>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if config.blockName }}
|
{{#if config.blockName }}
|
||||||
<script src="/scripts/{{ config.blockName }}.min.js"></script>
|
<script id="block-script" src="{{staticFilesPath}}/scripts/{{ config.blockName }}.min.js?v=1"></script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -3,12 +3,18 @@
|
|||||||
<div class="page_toolbar__left"></div>
|
<div class="page_toolbar__left"></div>
|
||||||
|
|
||||||
<div class="page_toolbar__middle">
|
<div class="page_toolbar__middle">
|
||||||
<div>
|
<div style="display: none">
|
||||||
Sizes: <b>1rem = {{ config.remToPx }}px</b>
|
Version: {{ config.version }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page_toolbar__right">
|
<div class="page_toolbar__right">
|
||||||
<a href="{{ previewFrameUrl }}" target="_blank" class="open_in_new_tab"></a>
|
{{#if shareUrl}}
|
||||||
|
<a href="{{ shareUrl }}" target="_blank" class="share" title="Share URL"></a>
|
||||||
|
{{/if}}
|
||||||
|
{{#if styleGuideUrl}}
|
||||||
|
<a href="{{ styleGuideUrl }}" target="_blank" class="palette" title="Open Style Guide"></a>
|
||||||
|
{{/if}}
|
||||||
|
<a href="{{ previewFrameUrl }}?block={{blockName}}" target="_blank" class="open_in_new_tab" title="Open in New Window"></a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
let e;window.initBlock=function(e="",n="",o){t=function(){document.querySelectorAll(n).forEach((e=>o(e)))},t()};let t,n={};!function(){const o=r().block;function r(){const e=new URLSearchParams(window.location.search),t={};for(const[n,o]of e)t[n]=o;return t}function a(e){const t=document.getElementById(e),n=t.parentNode;let o,r;return"SCRIPT"===t.tagName?(o=document.createElement("script"),o.type="text/javascript",r="src"):"LINK"===t.tagName&&(o=document.createElement("link"),o.rel="stylesheet",r="href"),o.id=e,o[r]=t[r].replace(/(\?v=)[^&]+/,`$1${Date.now()}`),t.remove(),n.append(o),o}function c(r={}){r.template&&(e=r.template),r.data&&(n=r.data),e&&function(e,n,r){const a=Handlebars.compile(e);let c;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(){const e=["block"];return o&&e.push(o.substr(1)),["",...e,""].join("/")}));try{c=a(n)}catch(e){c=`<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=c,t&&t()}(e,n||{},document.getElementById("hbs-container"))}!function(){const e=new URLSearchParams({block:o,name:r().data||"default"});fetch(`/data?${e}`).then((e=>e.json())).then((e=>{n=e.data,c({data:n})}))}(),window.addEventListener("message",(function(e){const t=e.data,o="dataUpdate:";if("string"==typeof t&&t.startsWith(o))try{n=JSON.parse(t.substring(o.length)),c({data:n})}catch(e){console.log("Error parsing incoming data.",e)}})),function(){const e=window.io.connect("",{query:`block=${o}`});e.on("error",(function(e){console.log(e)})),e.on("templateUpdate",(function(e){c({template:e.template})})),e.on("scriptUpdate",(function(e){a("block-script").onload=()=>{c({template:e.template})}})),e.on("styleUpdate",(function(e){a("block-stylesheet")}))}()}();
|
||||||
+1
File diff suppressed because one or more lines are too long
Vendored
+33
-9822
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+21
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M24 45.25q-4.35 0-8.225-1.675T9 39q-2.9-2.9-4.575-6.775Q2.75 28.35 2.75 24q0-4.5 1.675-8.375t4.625-6.75Q12 6 15.95 4.35q3.95-1.65 8.4-1.65 4.2 0 8 1.425t6.675 3.95Q41.9 10.6 43.6 14.05q1.7 3.45 1.7 7.5 0 5.7-3.2 9.35-3.2 3.65-8.9 3.65h-2.9q-.8 0-1.4.65-.6.65-.6 1.45 0 1.25.5 1.75t.5 1.55q0 2.2-1.5 3.75-1.5 1.55-3.8 1.55ZM24 24Zm-11.2 1.3q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1 0-1.75.75t-.75 1.75q0 1 .75 1.75t1.75.75Zm6.1-8.15q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm10.25 0q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm6.2 8.15q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1.05 0-1.775.75-.725.75-.725 1.75t.725 1.75q.725.75 1.775.75ZM23.8 40.55q.4 0 .6-.175.2-.175.2-.625 0-.7-.75-1.175-.75-.475-.75-2.375 0-2.45 1.65-4.4 1.65-1.95 4.1-1.95h4.35q3.7 0 5.525-2.2 1.825-2.2 1.825-5.8 0-6.6-4.925-10.5Q30.7 7.45 24.4 7.45q-7.1 0-12.025 4.825Q7.45 17.1 7.45 24q0 6.9 4.8 11.725 4.8 4.825 11.55 4.825Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="478" height="506" viewBox="0 0 478 506" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M379.8 315.4C352.01 315.4 327.062 327.817 310.146 347.315L186.499 278.249C188.838 270.119 190.1 261.521 190.1 252.7C190.1 243.782 188.834 235.223 186.427 227.037L309.923 158.096C326.774 177.698 351.821 190.2 379.7 190.2C430.409 190.2 471.8 148.918 471.8 98.1C471.8 47.2863 430.514 6 379.7 6C328.886 6 287.6 47.2863 287.6 98.1C287.6 106.995 288.859 115.615 291.265 123.769L167.886 192.697C151.025 173.006 125.968 160.6 98.1 160.6C47.3908 160.6 6 201.882 6 252.7C6 303.523 47.3954 344.8 98.2 344.8C126.093 344.8 151.144 332.287 168.074 312.598L291.571 381.633C289.143 389.853 287.8 398.557 287.8 407.5C287.8 458.209 329.082 499.6 379.9 499.6C430.714 499.6 472 458.314 472 407.5C472 356.677 430.605 315.4 379.8 315.4ZM379.8 45.1C409.086 45.1 432.9 68.9137 432.9 98.2C432.9 127.486 409.086 151.3 379.8 151.3C350.514 151.3 326.7 127.486 326.7 98.2C326.7 68.9203 350.607 45.1 379.8 45.1ZM98.2 305.8C68.9137 305.8 45.1 281.986 45.1 252.7C45.1 223.414 68.9137 199.6 98.2 199.6C127.486 199.6 151.3 223.414 151.3 252.7C151.3 281.98 127.393 305.8 98.2 305.8ZM379.8 460.5C350.514 460.5 326.7 436.686 326.7 407.4C326.7 378.114 350.514 354.3 379.8 354.3C409.086 354.3 432.9 378.114 432.9 407.4C432.9 436.686 409.086 460.5 379.8 460.5Z" fill="#21252D" stroke="#21252D" stroke-width="12"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,27 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const heightLimit = window.outerHeight * 2;
|
|
||||||
let height = getCurrentHeight();
|
|
||||||
setupResizeListener();
|
|
||||||
|
|
||||||
///
|
|
||||||
|
|
||||||
function setupResizeListener() {
|
|
||||||
const resizeObserver = new ResizeObserver(handleHeightChange);
|
|
||||||
resizeObserver.observe(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleHeightChange(entries) {
|
|
||||||
const updatedHeight = getCurrentHeight();
|
|
||||||
if (height === updatedHeight || updatedHeight > heightLimit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RESIZE_CODE = 'resize:';
|
|
||||||
window.parent.postMessage(RESIZE_CODE + JSON.stringify({height}), '*');
|
|
||||||
height = updatedHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentHeight() {
|
|
||||||
return document.querySelector('body > main').scrollHeight;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data Updates Listeners.
|
||||||
|
(function () {
|
||||||
|
const block = getQueryParams().block;
|
||||||
|
|
||||||
|
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({
|
||||||
|
block,
|
||||||
|
name: getQueryParams().data || 'default'
|
||||||
|
});
|
||||||
|
fetch(`/data?${queryParameters}`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((response) => {
|
||||||
|
data = response.data; // Update state.
|
||||||
|
updateBlock({data});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to Template updates.
|
||||||
|
initSocket();
|
||||||
|
|
||||||
|
function initSocket() {
|
||||||
|
const socket = window.io.connect('', {query: `block=${block}`});
|
||||||
|
|
||||||
|
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});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('scriptUpdate', function (args) {
|
||||||
|
const tag = updateTag('block-script');
|
||||||
|
tag.onload = () => {
|
||||||
|
updateBlock({template: args.template});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('styleUpdate', function (args) {
|
||||||
|
updateTag('block-stylesheet');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTag(id) {
|
||||||
|
const tag = document.getElementById(id);
|
||||||
|
const wrapper = tag.parentNode;
|
||||||
|
|
||||||
|
let clone;
|
||||||
|
let attr;
|
||||||
|
if (tag.tagName === 'SCRIPT') {
|
||||||
|
clone = document.createElement('script');
|
||||||
|
clone.type = 'text/javascript';
|
||||||
|
attr = 'src';
|
||||||
|
} else if (tag.tagName === 'LINK') {
|
||||||
|
clone = document.createElement('link');
|
||||||
|
clone.rel = 'stylesheet';
|
||||||
|
attr = 'href';
|
||||||
|
}
|
||||||
|
|
||||||
|
clone.id = id;
|
||||||
|
|
||||||
|
// Add version to the stylesheet URL, make sure we override the cache and already existing version there.
|
||||||
|
clone[attr] = tag[attr].replace(/(\?v=)[^&]+/, `$1${Date.now()}`);
|
||||||
|
|
||||||
|
tag.remove();
|
||||||
|
wrapper.append(clone);
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
const list = ['block'];
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
list.push(block.substr(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['', ...list, ''].join('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -4,21 +4,15 @@ import {setupResponsiveness} from './toolbar/responsive.jsx';
|
|||||||
import {setupPublish} from "./toolbar/publish.jsx";
|
import {setupPublish} from "./toolbar/publish.jsx";
|
||||||
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
|
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
|
||||||
|
|
||||||
const previewFrame = document.getElementById('preview_frame');
|
|
||||||
const rootAttributes = {
|
const rootAttributes = {
|
||||||
previewFrame,
|
previewFrame: getPreviewFrame(),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// setupFrameResizeListener();
|
||||||
setupResponsiveness(rootAttributes);
|
setupResponsiveness(rootAttributes);
|
||||||
setupDataOptions(rootAttributes);
|
setupDataOptions(rootAttributes);
|
||||||
setupPublish(rootAttributes)
|
setupPublish(rootAttributes);
|
||||||
|
|
||||||
window.addEventListener('message', function (e) {
|
function getPreviewFrame() {
|
||||||
const RESIZE_CODE = 'resize:';
|
return document.getElementById('preview_frame');
|
||||||
if (typeof e.data !== 'string' || !e.data.startsWith(RESIZE_CODE)) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.parse(e.data.substring(RESIZE_CODE.length))
|
|
||||||
previewFrame.style.height = data.height + 'px';
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
'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() {
|
||||||
|
// Add "ignore" query parameter to current URl and redirect.
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set('ignoreVersionSync', 'true');
|
||||||
|
window.location = url.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSyncLogic() {
|
||||||
|
setLoading('syncing');
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
try {
|
||||||
|
const response = await fetch('/sync', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({block: getQueryParams().block}),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This function is used in multiple places (repeated code).
|
||||||
|
// Move to a common place.
|
||||||
|
function getQueryParams() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
for (const [key, value] of urlParams) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
@@ -9,10 +9,21 @@ import {
|
|||||||
import {isClickOutside, isEscHit} from "../responsive-button/ResponsiveButton.jsx";
|
import {isClickOutside, isEscHit} from "../responsive-button/ResponsiveButton.jsx";
|
||||||
import {DesignPreview} from "./DesignPreview.jsx";
|
import {DesignPreview} from "./DesignPreview.jsx";
|
||||||
|
|
||||||
|
export const PRODUCTION_REGISTRY_URL = 'https://blocks-registery.axe-web.com';
|
||||||
|
|
||||||
function DataOptions(props = {}) {
|
function DataOptions(props = {}) {
|
||||||
props.rootAttributes = props.rootAttributes ?? {};
|
props.rootAttributes = props.rootAttributes ?? {};
|
||||||
|
|
||||||
const initialState = {dataName: 'default', data: {}, dataOptions: [], designPreview: []};
|
const initialState = {
|
||||||
|
dataName: 'default',
|
||||||
|
data: {},
|
||||||
|
dataText: '{}',
|
||||||
|
dataOptions: [],
|
||||||
|
designPreview: [],
|
||||||
|
errorMessage: null,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
const [state, setState] = useState(initialState);
|
const [state, setState] = useState(initialState);
|
||||||
const [previewOption, setPreviewOption] = useState(getDesignPreviewImage(state.dataName, state.designPreview));
|
const [previewOption, setPreviewOption] = useState(getDesignPreviewImage(state.dataName, state.designPreview));
|
||||||
const updateState = (update) => setState(Object.assign({}, state, update));
|
const updateState = (update) => setState(Object.assign({}, state, update));
|
||||||
@@ -20,8 +31,7 @@ function DataOptions(props = {}) {
|
|||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
const data = await fetchDataOptions(state.dataName);
|
await syncDataOptions(state.dataName);
|
||||||
updateState(data);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCloseSidebarEscEvent = useCallback((e) => {
|
const handleCloseSidebarEscEvent = useCallback((e) => {
|
||||||
@@ -66,7 +76,7 @@ function DataOptions(props = {}) {
|
|||||||
<SidebarDataOptionsStyle>
|
<SidebarDataOptionsStyle>
|
||||||
<label htmlFor="data-options">Data Options</label>
|
<label htmlFor="data-options">Data Options</label>
|
||||||
|
|
||||||
<select name="data" id="data-options" onChange={(e) => changeDataOption(e)} value={state.dataName}>
|
<select name="data" id="data-options" onChange={(e) => changeDataOption(e)} value={state.dataName} disabled={state.loading}>
|
||||||
{state.dataOptions.map((item) => {
|
{state.dataOptions.map((item) => {
|
||||||
const isSelected = state.dataName === item;
|
const isSelected = state.dataName === item;
|
||||||
return <option value={item} selected={isSelected}>{item}</option>
|
return <option value={item} selected={isSelected}>{item}</option>
|
||||||
@@ -76,10 +86,21 @@ function DataOptions(props = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{state.data &&
|
{state.data &&
|
||||||
<pre>{JSON.stringify(state.data, null, 2)}</pre>
|
<textarea value={state.dataText} onChange={dataOptionUpdate} disabled={state.loading}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<button className='btn btn--secondary' onClick={(e) => copyToClipboard(e, state.data)}>Copy to Clipboard</button>
|
{state.errorMessage &&
|
||||||
|
<p className={'alert alert--error'}>{state.errorMessage}</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
{state.loading &&
|
||||||
|
<p className={'alert'}>Loading, please wait...</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={'actions'}>
|
||||||
|
<button className='btn btn--secondary' disabled={state.loading} onClick={(e) => copyToClipboard(e, state.data)}>Copy to Clipboard</button>
|
||||||
|
<button className='btn btn--secondary' disabled={state.loading} onClick={(e) => generateVariation(e, state.data)}>Generate Test</button>
|
||||||
|
</div>
|
||||||
</SidebarStyle>
|
</SidebarStyle>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
@@ -87,6 +108,52 @@ function DataOptions(props = {}) {
|
|||||||
// Functions
|
// Functions
|
||||||
//
|
//
|
||||||
|
|
||||||
|
function generateVariation() {
|
||||||
|
const url = PRODUCTION_REGISTRY_URL + '/content-generator/';
|
||||||
|
|
||||||
|
const myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: JSON.stringify({json: state.data}),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateState({loading: true, errorMessage: null});
|
||||||
|
|
||||||
|
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, errorMessage: null, loading: false});
|
||||||
|
updateIframe(data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
updateState({loading: false, errorMessage: 'Something went wrong, please try again.'})
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataOptionUpdate(e) {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(e.target.value);
|
||||||
|
} catch (err) {
|
||||||
|
updateState({dataText: e.target.value, errorMessage: 'Invalid JSON, please review and try again.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState({dataText: e.target.value, data, errorMessage: null});
|
||||||
|
updateIframe(data);
|
||||||
|
}
|
||||||
|
|
||||||
function openSidebar() {
|
function openSidebar() {
|
||||||
setSidebarOpen(true);
|
setSidebarOpen(true);
|
||||||
setTimeout(() => document.querySelector('.sidebar-active').focus());
|
setTimeout(() => document.querySelector('.sidebar-active').focus());
|
||||||
@@ -98,14 +165,11 @@ function DataOptions(props = {}) {
|
|||||||
|
|
||||||
async function changeDataOption(e) {
|
async function changeDataOption(e) {
|
||||||
const dataName = e.target.value;
|
const dataName = e.target.value;
|
||||||
props.rootAttributes.previewFrame.src = window.devTool.previewFrameUrl + '?data=' + dataName;
|
await syncDataOptions(dataName);
|
||||||
|
|
||||||
const dataOption = await fetchDataOptions(dataName);
|
|
||||||
updateState(Object.assign({}, dataOption, {dataName}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDataOptions(name = 'default') {
|
async function fetchDataOptions(name = 'default') {
|
||||||
const queryParameters = new URLSearchParams({name});
|
const queryParameters = new URLSearchParams({name, block: window.devTool.blockName});
|
||||||
const response = await fetch(`/data?${queryParameters}`);
|
const response = await fetch(`/data?${queryParameters}`);
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
@@ -133,6 +197,23 @@ function DataOptions(props = {}) {
|
|||||||
|
|
||||||
setPreviewOption(getDesignPreviewImage(state.dataName, state.designPreview));
|
setPreviewOption(getDesignPreviewImage(state.dataName, state.designPreview));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function syncDataOptions(dataName) {
|
||||||
|
const dataOptions = await fetchDataOptions(dataName);
|
||||||
|
updateIframe(dataOptions.data);
|
||||||
|
|
||||||
|
const newState = Object.assign({errorMessage: null},
|
||||||
|
dataOptions,
|
||||||
|
{dataName},
|
||||||
|
{dataText: JSON.stringify(dataOptions.data, null, 2)},
|
||||||
|
);
|
||||||
|
updateState(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateIframe(data) {
|
||||||
|
const previewIframe = props.rootAttributes.previewFrame;
|
||||||
|
previewIframe.contentWindow.postMessage('dataUpdate:' + JSON.stringify(data || {}), '*');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyToClipboard(e, context) {
|
function copyToClipboard(e, context) {
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ export const SidebarStyle = styled.div`
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre, textarea {
|
||||||
|
height: 100%;
|
||||||
|
//min-height: 480px;
|
||||||
|
resize: vertical;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background-color: #EDF2F7;
|
background-color: #EDF2F7;
|
||||||
@@ -33,6 +36,20 @@ export const SidebarStyle = styled.div`
|
|||||||
border: 1px solid #cbd5e0;
|
border: 1px solid #cbd5e0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
display: block;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.alert--error {
|
||||||
|
color: red;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M24 45.25q-4.35 0-8.225-1.675T9 39q-2.9-2.9-4.575-6.775Q2.75 28.35 2.75 24q0-4.5 1.675-8.375t4.625-6.75Q12 6 15.95 4.35q3.95-1.65 8.4-1.65 4.2 0 8 1.425t6.675 3.95Q41.9 10.6 43.6 14.05q1.7 3.45 1.7 7.5 0 5.7-3.2 9.35-3.2 3.65-8.9 3.65h-2.9q-.8 0-1.4.65-.6.65-.6 1.45 0 1.25.5 1.75t.5 1.55q0 2.2-1.5 3.75-1.5 1.55-3.8 1.55ZM24 24Zm-11.2 1.3q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1 0-1.75.75t-.75 1.75q0 1 .75 1.75t1.75.75Zm6.1-8.15q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm10.25 0q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm6.2 8.15q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1.05 0-1.775.75-.725.75-.725 1.75t.725 1.75q.725.75 1.775.75ZM23.8 40.55q.4 0 .6-.175.2-.175.2-.625 0-.7-.75-1.175-.75-.475-.75-2.375 0-2.45 1.65-4.4 1.65-1.95 4.1-1.95h4.35q3.7 0 5.525-2.2 1.825-2.2 1.825-5.8 0-6.6-4.925-10.5Q30.7 7.45 24.4 7.45q-7.1 0-12.025 4.825Q7.45 17.1 7.45 24q0 6.9 4.8 11.725 4.8 4.825 11.55 4.825Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="478" height="506" viewBox="0 0 478 506" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M379.8 315.4C352.01 315.4 327.062 327.817 310.146 347.315L186.499 278.249C188.838 270.119 190.1 261.521 190.1 252.7C190.1 243.782 188.834 235.223 186.427 227.037L309.923 158.096C326.774 177.698 351.821 190.2 379.7 190.2C430.409 190.2 471.8 148.918 471.8 98.1C471.8 47.2863 430.514 6 379.7 6C328.886 6 287.6 47.2863 287.6 98.1C287.6 106.995 288.859 115.615 291.265 123.769L167.886 192.697C151.025 173.006 125.968 160.6 98.1 160.6C47.3908 160.6 6 201.882 6 252.7C6 303.523 47.3954 344.8 98.2 344.8C126.093 344.8 151.144 332.287 168.074 312.598L291.571 381.633C289.143 389.853 287.8 398.557 287.8 407.5C287.8 458.209 329.082 499.6 379.9 499.6C430.714 499.6 472 458.314 472 407.5C472 356.677 430.605 315.4 379.8 315.4ZM379.8 45.1C409.086 45.1 432.9 68.9137 432.9 98.2C432.9 127.486 409.086 151.3 379.8 151.3C350.514 151.3 326.7 127.486 326.7 98.2C326.7 68.9203 350.607 45.1 379.8 45.1ZM98.2 305.8C68.9137 305.8 45.1 281.986 45.1 252.7C45.1 223.414 68.9137 199.6 98.2 199.6C127.486 199.6 151.3 223.414 151.3 252.7C151.3 281.98 127.393 305.8 98.2 305.8ZM379.8 460.5C350.514 460.5 326.7 436.686 326.7 407.4C326.7 378.114 350.514 354.3 379.8 354.3C409.086 354.3 432.9 378.114 432.9 407.4C432.9 436.686 409.086 460.5 379.8 460.5Z" fill="#21252D" stroke="#21252D" stroke-width="12"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,10 +1,14 @@
|
|||||||
import React, {useEffect, useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
import * as ReactDOM from 'react-dom/client';
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
function Publish(props = {}) {
|
function Publish(props = {}) {
|
||||||
const [state, setState] = useState({loading: false});
|
const [state, setState] = useState({loading: false});
|
||||||
const updateState = (update) => setState(Object.assign({}, state, update));
|
const updateState = (update) => setState(Object.assign({}, state, update));
|
||||||
|
|
||||||
|
if (window.devTool.publicUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{state.loading &&
|
{state.loading &&
|
||||||
<div className="overlay overlay--loading">Loading, Please wait...</div>
|
<div className="overlay overlay--loading">Loading, Please wait...</div>
|
||||||
@@ -39,6 +43,7 @@ function Publish(props = {}) {
|
|||||||
export function setupPublish(rootAttributes) {
|
export function setupPublish(rootAttributes) {
|
||||||
// INIT
|
// INIT
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.setAttribute('id', 'publish-btn');
|
||||||
document.querySelector('.page_toolbar__right').append(wrapper)
|
document.querySelector('.page_toolbar__right').append(wrapper)
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(wrapper);
|
const root = ReactDOM.createRoot(wrapper);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {ButtonStyling, ResponsiveButtonStyle, ResponsiveOptionsDropdown} from ".
|
|||||||
const responsiveOptions = {
|
const responsiveOptions = {
|
||||||
desktop: [1920, 1800, 1680, 1440, 1360, 1280, 1024],
|
desktop: [1920, 1800, 1680, 1440, 1360, 1280, 1024],
|
||||||
tablet: [992, 768, 600],
|
tablet: [992, 768, 600],
|
||||||
mobile: [480, 414, 375, 360],
|
mobile: [480, 414, 375, 360, 320],
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultResponsiveOptions = {
|
const defaultResponsiveOptions = {
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ function Responsive(props = {}) {
|
|||||||
function updateController() {
|
function updateController() {
|
||||||
let frameBreakpoint = breakpoint;
|
let frameBreakpoint = breakpoint;
|
||||||
if (typeof frameBreakpoint !== 'string') {
|
if (typeof frameBreakpoint !== 'string') {
|
||||||
frameBreakpoint = frameBreakpoint + 'px';
|
const scrollbarWidth = 15;
|
||||||
|
frameBreakpoint = (scrollbarWidth + frameBreakpoint) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
previewFrame.style.setProperty('--breakpoint', frameBreakpoint);
|
previewFrame.style.setProperty('--breakpoint', frameBreakpoint);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<section class="fullscreen_layout"></section>
|
||||||
|
<div id="hbs-container"></div>
|
||||||
|
<section class="fullscreen_layout"></section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
.preview {
|
.preview {
|
||||||
overflow-y: scroll;
|
|
||||||
height: calc(100% - var(--top_panel_height));
|
height: calc(100% - var(--top_panel_height));
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -9,17 +8,14 @@
|
|||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
//height: 100%;
|
||||||
|
|
||||||
--top_spacing: 0px;
|
--top_spacing: 0px;
|
||||||
--breakpoint_top_spacing: 30px;
|
--breakpoint_top_spacing: 30px;
|
||||||
|
|
||||||
margin-top: var(--top_spacing);
|
margin-top: var(--top_spacing);
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
||||||
border: 0;
|
border: 0;
|
||||||
outline: 1px solid #E2E8F0;
|
|
||||||
|
|
||||||
transition: max-width .3s ease-in-out, width .3s ease-in-out, margin-top .3s ease-in-out;
|
transition: max-width .3s ease-in-out, width .3s ease-in-out, margin-top .3s ease-in-out;
|
||||||
|
|
||||||
&.has-breakpoint {
|
&.has-breakpoint {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ body {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
background-color: #F7FAFC;
|
background-color: #F7FAFC;
|
||||||
}
|
}
|
||||||
|
body.view-mode #publish-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
--btn-color: #333;
|
--btn-color: #333;
|
||||||
@@ -114,7 +117,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
overflow-y: scroll;
|
|
||||||
height: calc(100% - var(--top_panel_height));
|
height: calc(100% - var(--top_panel_height));
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -127,10 +129,8 @@ body {
|
|||||||
--top_spacing: 0px;
|
--top_spacing: 0px;
|
||||||
--breakpoint_top_spacing: 30px;
|
--breakpoint_top_spacing: 30px;
|
||||||
margin-top: var(--top_spacing);
|
margin-top: var(--top_spacing);
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 0;
|
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;
|
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 {
|
#preview_frame.has-breakpoint {
|
||||||
@@ -139,11 +139,11 @@ body {
|
|||||||
max-width: var(--breakpoint);
|
max-width: var(--breakpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.open_in_new_tab {
|
.open_in_new_tab, .share, .palette {
|
||||||
--size: 1.5rem;
|
--size: 1.5rem;
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
background-image: url("/scripts/toolbar/images/icon-open-new-tab.svg");
|
background-image: url("/scripts/dist/toolbar/images/icon-open-new-tab.svg");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: calc(var(--size) - 0.15rem);
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
@@ -158,4 +158,13 @@ body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.palette {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-palette.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.share {
|
||||||
|
background-size: calc(var(--size) - 0.35rem);
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-share.svg");
|
||||||
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=page--main.css.map */
|
/*# sourceMappingURL=page--main.css.map */
|
||||||
|
|||||||
@@ -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","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;;AAGE;EACE;;;ACbN;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;;;AFUF;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;;;AGxFJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EACA;EACA;;AAEA;EACE;EAEA;EACA;;;AHuEJ;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"}
|
||||||
@@ -8,6 +8,12 @@ body {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
//overflow: none;
|
//overflow: none;
|
||||||
background-color: #F7FAFC;
|
background-color: #F7FAFC;
|
||||||
|
|
||||||
|
&.view-mode {
|
||||||
|
#publish-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "buttons";
|
@import "buttons";
|
||||||
@@ -92,7 +98,7 @@ body {
|
|||||||
width: var(--size);
|
width: var(--size);
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
|
|
||||||
background-image: url("/scripts/toolbar/images/icon-open-new-tab.svg");
|
background-image: url("/scripts/dist/toolbar/images/icon-open-new-tab.svg");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: calc(var(--size) - 0.15rem);
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
@@ -107,3 +113,14 @@ body {
|
|||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.palette {
|
||||||
|
@extend .open_in_new_tab;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-palette.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.share {
|
||||||
|
@extend .open_in_new_tab;
|
||||||
|
background-size: calc(var(--size) - 0.35rem);
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-share.svg");
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
.body--iframe {
|
||||||
margin-left: auto;
|
overflow-y: scroll;
|
||||||
margin-right: auto;
|
}
|
||||||
|
|
||||||
|
.fullscreen_layout {
|
||||||
|
background-color: #9cc3ff;
|
||||||
|
min-height: 100%;
|
||||||
|
background-image: url("https://i.ibb.co/pjwL8D1/shapelined-JBKdviwe-XI-unsplash.jpg");
|
||||||
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=page--view.css.map */
|
/*# sourceMappingURL=page--view.css.map */
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"sourceRoot":"","sources":["page--view.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;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 +1,15 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
// iFrame mode
|
||||||
margin-left: auto;
|
.body--iframe {
|
||||||
margin-right: auto;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container rules must be provided by the projectStyles (theme).
|
.fullscreen_layout {
|
||||||
//.container {
|
background-color: #9cc3ff;
|
||||||
// max-width: 1200px;
|
min-height: 100%;
|
||||||
// margin-left: auto;
|
background-image: url('https://i.ibb.co/pjwL8D1/shapelined-JBKdviwe-XI-unsplash.jpg');
|
||||||
// margin-right: auto;
|
background-size: cover;
|
||||||
// padding-left: 15px;
|
}
|
||||||
// padding-right: 15px;
|
|
||||||
//}
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<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>
|
||||||
Generated
+1158
-12959
File diff suppressed because it is too large
Load Diff
+29
-11
@@ -1,27 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "@axe-web/block-dev-tool",
|
"name": "@axe-web/block-dev-tool",
|
||||||
"version": "1.0.19",
|
"version": "1.0.31",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "AXE-WEB",
|
"name": "AXE-WEB",
|
||||||
"email": "office@axe-web.com",
|
"email": "office@axe-web.com",
|
||||||
"url": "https://axe-web.com/"
|
"url": "https://axe-web.com/"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "component-dev",
|
"info": "node debug.js",
|
||||||
"dev": "NODE_ENV=development NODE_CONFIG_DIR=blocks/team/config BLOCK_NAME=team node server.js",
|
"dev": "node server.js",
|
||||||
"build": "rollup --config rollup.config.js",
|
"view-mode": "VIEW_MODE=true node server.js",
|
||||||
"build-platform": "NODE_ENV=development NODE_CONFIG_DIR=blocks/team/config BLOCK_NAME=team node ./build.js",
|
"build-platform": "BLOCK_NAME=swiper-test MODULE_PATH= node ./build.js",
|
||||||
"build-platform-cli": "component-build",
|
"dev-dev-tool": "rollup --config rollup.config.js --watch",
|
||||||
"dev-js": "rollup --config rollup.config.js --watch"
|
"build-dev-tool": "rollup --config rollup.config.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17.3"
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@axe-web/create-block": "^1.1.30",
|
||||||
"@braintree/sanitize-url": "^6.0.0",
|
"@braintree/sanitize-url": "^6.0.0",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
"browser-sync": "^2.27.9",
|
"browser-sync": "^2.27.9",
|
||||||
"config": "^3.3.7",
|
"config": "^3.3.7",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
|
"exec-php": "^0.0.6",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-handlebars": "^6.0.4",
|
"express-handlebars": "^6.0.4",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
@@ -34,13 +40,15 @@
|
|||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mem-fs": "^2.2.1",
|
"mem-fs": "^2.2.1",
|
||||||
"mem-fs-editor": "^9.5.0",
|
"mem-fs-editor": "^9.5.0",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
|
"ngrok": "^5.0.0-beta.2",
|
||||||
"node-fetch": "^3.2.10",
|
"node-fetch": "^3.2.10",
|
||||||
"open": "^8.4.0",
|
"open": "^8.4.0",
|
||||||
"plugin-error": "^2.0.0",
|
"plugin-error": "^2.0.0",
|
||||||
|
"prompts": "^2.4.2",
|
||||||
"sanitize-html": "^2.7.1",
|
"sanitize-html": "^2.7.1",
|
||||||
"sass": "^1.50.1",
|
"sass": "^1.50.1",
|
||||||
"yeoman-environment": "^3.10.0",
|
"ws": "^8.13.0"
|
||||||
"yeoman-generator": "^5.6.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.18.6",
|
||||||
@@ -54,15 +62,25 @@
|
|||||||
"rollup": "^2.77.2",
|
"rollup": "^2.77.2",
|
||||||
"rollup-plugin-copy": "^3.4.0",
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
"rollup-plugin-jsx": "^1.0.3",
|
"rollup-plugin-jsx": "^1.0.3",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"socket.io": "^4.6.2",
|
||||||
"styled-components": "^5.3.5"
|
"styled-components": "^5.3.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"component-dev": "./server.js",
|
"component-dev": "./server.js",
|
||||||
"component-build": "./build.js"
|
"component-build": "./build.js",
|
||||||
|
"component-info": "./debug.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
"env.js",
|
||||||
"helpers.js",
|
"helpers.js",
|
||||||
"layouts/**/*",
|
"debug.js",
|
||||||
|
"layouts/**/*.hbs",
|
||||||
|
"layouts/styles/*.css*",
|
||||||
|
"layouts/scripts/dist/*.js*",
|
||||||
|
"layouts/scripts/dist/**/*.jpg",
|
||||||
|
"layouts/scripts/dist/**/*.png",
|
||||||
|
"layouts/scripts/dist/**/*.svg",
|
||||||
"platforms/**/*"
|
"platforms/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,35 @@
|
|||||||
|
import path from "path";
|
||||||
import {readFile, writeFile, mkdir, copyFile} from "fs/promises";
|
import {readFile, writeFile, mkdir, copyFile} from "fs/promises";
|
||||||
|
import {capitalize, getConfigs} from "../../helpers.js";
|
||||||
|
|
||||||
export async function buildHubspot(blockName) {
|
export async function buildHubspotEmail(blockName) {
|
||||||
const distPath = `./exports/hubspot/${blockName}.module`;
|
const distPath = await createDistFolder(blockName);
|
||||||
await mkdir(distPath, {recursive: true})
|
|
||||||
await copyFile(`./src/${blockName}.template.hbs`, `${distPath}/module.html`)
|
|
||||||
|
|
||||||
const metaData = {
|
// Template
|
||||||
|
let handlebars = await readFile(path.join(srcPath, `${blockName}.template.hbs`), "utf8");
|
||||||
|
await writeFile(path.join(distPath, 'module.html'), handlebarsToHubl(handlebars));
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
await buildHubspotJSONFiles(distPath, {
|
||||||
global: false,
|
global: false,
|
||||||
host_template_types: ["EMAIL"],
|
host_template_types: ["EMAIL"],
|
||||||
label: capitalize(blockName),
|
label: capitalize(blockName),
|
||||||
is_available_for_new_content: true
|
is_available_for_new_content: true
|
||||||
}
|
});
|
||||||
|
|
||||||
await writeFile(`${distPath}/meta.json`, JSON.stringify(metaData, null, 4));
|
|
||||||
|
|
||||||
const blockJSON = await readFile(`./block.json`, "utf8");
|
|
||||||
const block = JSON.parse(blockJSON);
|
|
||||||
|
|
||||||
const fields = getBlockFields(block, 'content');
|
|
||||||
|
|
||||||
// Styling TAB.
|
|
||||||
const stylingFields = getBlockFields(block, 'styling');
|
|
||||||
|
|
||||||
if (stylingFields.length) {
|
|
||||||
const stylingFieldsByName = {};
|
|
||||||
stylingFields.forEach(field => stylingFieldsByName[field.name] = field);
|
|
||||||
|
|
||||||
const stylingGroup = convertToHubspotField({
|
|
||||||
type: 'group',
|
|
||||||
name: 'style',
|
|
||||||
label: "Style",
|
|
||||||
sub_fields: stylingFieldsByName,
|
|
||||||
});
|
|
||||||
stylingGroup.tab = "STYLE";
|
|
||||||
|
|
||||||
fields.push(stylingGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export JSON file.
|
|
||||||
await writeFile(`${distPath}/fields.json`, JSON.stringify(fields, null, 4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBlockFields(block = {}, type = 'content') {
|
export async function createDistFolder(blockName, projectPath = '') {
|
||||||
|
const distPath = path.join(projectPath, 'exports', 'hubspot', `${blockName}.module`);
|
||||||
|
await mkdir(distPath, {recursive: true})
|
||||||
|
|
||||||
|
return distPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlockFields(block = {}, type = 'content') {
|
||||||
|
if (!block['field_groups']) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const fields_group = block['field_groups'].find((group) => group.name === type);
|
const fields_group = block['field_groups'].find((group) => group.name === type);
|
||||||
const fields = [];
|
const fields = [];
|
||||||
|
|
||||||
@@ -61,7 +49,7 @@ function getBlockFields(block = {}, type = 'content') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToHubspotField(field = {}) {
|
export function convertToHubspotField(field = {}) {
|
||||||
const data = {
|
const data = {
|
||||||
id: field.name,
|
id: field.name,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
@@ -70,9 +58,12 @@ function convertToHubspotField(field = {}) {
|
|||||||
validation_regex: "",
|
validation_regex: "",
|
||||||
required: false,
|
required: false,
|
||||||
locked: false,
|
locked: false,
|
||||||
default: field.default
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (field.default) {
|
||||||
|
data.default = field.default;
|
||||||
|
}
|
||||||
|
|
||||||
let sub_fields = [];
|
let sub_fields = [];
|
||||||
|
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
@@ -111,12 +102,14 @@ function convertToHubspotField(field = {}) {
|
|||||||
display: "checkbox",
|
display: "checkbox",
|
||||||
});
|
});
|
||||||
case 'select':
|
case 'select':
|
||||||
const choices = [];
|
const options = [];
|
||||||
Object.keys(data.choices).forEach(value => choices.push([value, data.choices[value]]));
|
if (field.options) {
|
||||||
|
Object.keys(field.options).forEach(value => options.push([value, field.options[value]]));
|
||||||
|
}
|
||||||
|
|
||||||
return Object.assign({}, data, {
|
return Object.assign({}, data, {
|
||||||
type: "select",
|
type: "choice",
|
||||||
choices: [choices]
|
choices: options
|
||||||
});
|
});
|
||||||
case 'link':
|
case 'link':
|
||||||
return Object.assign({}, data, {
|
return Object.assign({}, data, {
|
||||||
@@ -225,17 +218,57 @@ function convertToHubspotField(field = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function capitalize(str) {
|
export async function buildHubspotJSONFiles(distPath, metaData) {
|
||||||
if (typeof str !== 'string') {
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
return '';
|
await writeFile(path.join(distPath, 'meta.json'), JSON.stringify(metaData, null, 4));
|
||||||
|
|
||||||
|
const blockJSON = await readFile(path.join(projectPath, 'block.json'), "utf8");
|
||||||
|
const block = JSON.parse(blockJSON);
|
||||||
|
|
||||||
|
const fields = getBlockFields(block, 'content');
|
||||||
|
|
||||||
|
// Styling TAB.
|
||||||
|
const stylingFields = getBlockFields(block, 'styling');
|
||||||
|
|
||||||
|
if (stylingFields.length) {
|
||||||
|
const stylingFieldsByName = {};
|
||||||
|
stylingFields.forEach(field => stylingFieldsByName[field.name] = field);
|
||||||
|
|
||||||
|
const stylingGroup = convertToHubspotField({
|
||||||
|
type: 'group',
|
||||||
|
name: 'style',
|
||||||
|
label: "Style",
|
||||||
|
});
|
||||||
|
|
||||||
|
stylingGroup.children = Object.values(stylingFieldsByName);
|
||||||
|
stylingGroup.tab = "STYLE";
|
||||||
|
|
||||||
|
fields.push(stylingGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
// Export JSON file.
|
||||||
.toLowerCase()
|
await writeFile(path.join(distPath, 'fields.json'), JSON.stringify(fields, null, 4));
|
||||||
.split(/[ -_]/g)
|
}
|
||||||
.filter((word) => !!word)
|
|
||||||
.map((word) => {
|
|
||||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
export function handlebarsToHubl(handlebars) {
|
||||||
})
|
handlebars = handlebars.replace(/{{ else }}/g, '{% else %}');
|
||||||
.join(' ');
|
handlebars = handlebars.replace(/{{else}}/g, '{% else %}');
|
||||||
|
handlebars = handlebars.replace(/{{#if /g, '{% if module.');
|
||||||
|
handlebars = handlebars.replace(/{{\/if}}/g, '{% endif %}');
|
||||||
|
handlebars = handlebars.replace(/{{#each /g, '{% for item in module.');
|
||||||
|
handlebars = handlebars.replace(/{{\/each}}/g, '{% endfor %}');
|
||||||
|
handlebars = handlebars.replace(/{{base_url}}/g, '');
|
||||||
|
handlebars = handlebars.replace(/{esc_attr /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{esc_url /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{esc_html /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{{{ /g, '{{');
|
||||||
|
handlebars = handlebars.replace(/ }}}/g, '}}');
|
||||||
|
handlebars = handlebars.replace(/{{{/g, '{{');
|
||||||
|
handlebars = handlebars.replace(/}}}/g, '}}');
|
||||||
|
handlebars = handlebars.replace(/{{/g, '{{module.');
|
||||||
|
handlebars = handlebars.replace(/{{module. /g, '{{ module.');
|
||||||
|
handlebars = handlebars.replace(/.url/g, '.src');
|
||||||
|
|
||||||
|
return handlebars;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import path from "path";
|
||||||
|
import {copyFile, readFile, writeFile} from "fs/promises";
|
||||||
|
import {capitalize, getConfigs} from "../../helpers.js";
|
||||||
|
import {buildHubspotJSONFiles, createDistFolder, handlebarsToHubl,} from "./hubspot-email-adapter.js";
|
||||||
|
|
||||||
|
export async function buildHubspotPage(blockName) {
|
||||||
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
|
const distPath = await createDistFolder(blockName, projectPath);
|
||||||
|
|
||||||
|
const srcPath = path.join(projectPath, 'src');
|
||||||
|
|
||||||
|
// Template
|
||||||
|
let handlebars = await readFile(path.join(srcPath, `${blockName}.template.hbs`), "utf8");
|
||||||
|
await writeFile(path.join(distPath, 'module.html'), handlebarsToHubl(handlebars));
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
await copyFile(path.join(srcPath, 'styles', `${blockName}.min.css`), path.join(distPath, 'module.css'));
|
||||||
|
await copyFile(path.join(srcPath, 'scripts', `${blockName}.min.js`), path.join(distPath, 'module.js'));
|
||||||
|
// await copy(path.join(projectPath, 'src', 'images'), path.join(distPath, 'images'));
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
await buildHubspotJSONFiles(distPath, {
|
||||||
|
global: false,
|
||||||
|
host_template_types: ["PAGE"],
|
||||||
|
label: capitalize(blockName),
|
||||||
|
is_available_for_new_content: true
|
||||||
|
});
|
||||||
|
}
|
||||||
+108
-83
@@ -3,124 +3,149 @@
|
|||||||
// Composer - Autoloader
|
// Composer - Autoloader
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Brick\VarExporter\ExportException;
|
||||||
use LightnCandy\Flags;
|
use LightnCandy\Flags;
|
||||||
use LightnCandy\LightnCandy;
|
use LightnCandy\LightnCandy;
|
||||||
|
use Brick\VarExporter\VarExporter;
|
||||||
|
|
||||||
trait Custom_Handlebars {
|
trait Custom_Handlebars {
|
||||||
public array $custom_handlebars = [];
|
public array $custom_handlebars = [];
|
||||||
|
|
||||||
public function register_default_handlebar_helpers() {
|
public function register_default_handlebar_helpers(): void {
|
||||||
$this->add_handlebar( 'esc_url', function ( $context ) {
|
$this->add_handlebar( 'esc_url', function ( $context ) {
|
||||||
if ( function_exists( 'esc_url' ) ) {
|
if ( function_exists( 'esc_url' ) ) {
|
||||||
return esc_url( $context );
|
return esc_url( $context );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$this->add_handlebar( 'esc_attr', function ( $context ) {
|
$this->add_handlebar( 'esc_attr', function ( $context ) {
|
||||||
if ( function_exists( 'esc_attr' ) ) {
|
if ( function_exists( 'esc_attr' ) ) {
|
||||||
return esc_attr( $context );
|
return esc_attr( $context );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$this->add_handlebar( 'esc_html', function ( $context ) {
|
$this->add_handlebar( 'esc_html', function ( $context ) {
|
||||||
if ( function_exists( 'esc_html' ) ) {
|
if ( function_exists( 'esc_html' ) ) {
|
||||||
return esc_html( $context );
|
return esc_html( $context );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$this->add_handlebar( 'safe_html', function ( $context ) {
|
$this->add_handlebar( 'safe_html', function ( $context ) {
|
||||||
if ( function_exists( 'wp_kses_post' ) ) {
|
if ( function_exists( 'wp_kses_post' ) ) {
|
||||||
return wp_kses_post( $context );
|
return wp_kses_post( $context );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add_handlebar( $key, $func ) {
|
public function add_handlebar( $key, $func ): void {
|
||||||
$this->custom_handlebars[ $key ] = $func;
|
$this->custom_handlebars[ $key ] = $func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Component_Builder {
|
class Component_Builder {
|
||||||
use Custom_Handlebars;
|
use Custom_Handlebars;
|
||||||
|
|
||||||
public string $component_name = '';
|
public string $component_name = '';
|
||||||
private string $module_path = '';
|
public string $module_path = '';
|
||||||
|
public string $project_path = '';
|
||||||
|
private string $dist_path = '';
|
||||||
|
|
||||||
function __construct( $component_name, $module_path, $project_path ) {
|
function __construct( $args = [] ) {
|
||||||
$this->module_path = $module_path;
|
if ( ! isset( $args['backPath'] ) || ! isset( $args['projectPath'] ) || ! isset( $args['blockName'] ) || ! isset( $args['platform'] ) ) {
|
||||||
$this->project_path = $project_path;
|
throw new \Exception( 'Error: Missing arguments. Make sure all parameter passed.' );
|
||||||
$this->component_name = $component_name;
|
}
|
||||||
$this->register_default_handlebar_helpers();
|
|
||||||
}
|
|
||||||
|
|
||||||
function build(): void {
|
$this->module_path = $args['backPath'];
|
||||||
$root_path = __DIR__ . '/' . $this->module_path . '/../../' . $this->project_path;
|
$this->project_path = $args['projectPath'];
|
||||||
$file_name = $this->get_handlebars_template( "$root_path/src/$this->component_name.template.hbs" );
|
$this->component_name = $args['blockName'];
|
||||||
|
|
||||||
$output_folder = $root_path . '/exports/wordpress/templates';
|
$this->dist_path = __DIR__ . '/' . $this->module_path . '/../../' . $this->project_path . '/exports' . '/' . $args['platform'];
|
||||||
foreach ( [ 'styles', 'scripts', 'images' ] as $dir ) {
|
$this->register_default_handlebar_helpers();
|
||||||
$output_dir = "$output_folder/$dir";
|
|
||||||
if ( is_dir( $output_dir ) === false ) {
|
|
||||||
mkdir( $output_dir, 0777, true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rename( $file_name, "$output_folder/$this->component_name.template.php" );
|
$this->add_handlebar( 'base_url', function ( $context ) {
|
||||||
copy( "$root_path/src/styles/$this->component_name.min.css", "$output_folder/styles/$this->component_name.min.css" );
|
$path = join( '/', [ $this->block_project, $this->block_name, 'templates' ] );
|
||||||
copy( "$root_path/src/scripts/$this->component_name.min.js", "$output_folder/scripts/$this->component_name.min.js" );
|
return join( '/', [ get_site_url(),'wp-content', 'axe-web-blocks', $path, '' ] );
|
||||||
shell_exec( "cp -r $root_path/src/images $output_folder" );
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
echo "Generated '$file_name'.";
|
function build(): void {
|
||||||
}
|
$root_path = __DIR__ . '/' . $this->module_path . '/../../' . $this->project_path;
|
||||||
|
|
||||||
private function get_handlebars_template( $path = '' ): string {
|
$this->buildTemplatePhpFile( $root_path );
|
||||||
$template = file_get_contents( $path );
|
}
|
||||||
$phpStr = LightnCandy::compile( $template,
|
|
||||||
[
|
|
||||||
'flags' => Flags::FLAG_NOESCAPE | Flags::FLAG_PARENT | Flags::FLAG_SPVARS | Flags::FLAG_ELSE | Flags::FLAG_JSLENGTH | Flags::FLAG_JSTRUE,
|
|
||||||
'helpers' => $this->custom_handlebars ?? [],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
private function buildTemplatePhpFile( $root_path ): void {
|
||||||
* NOTE:
|
$file_name = $this->get_handlebars_template( "$root_path/src/$this->component_name.template.hbs" );
|
||||||
* PHP 8.0.0 has problems with the LightCandy lib.
|
|
||||||
* If you're running the exact php8.0.0 version, try to downgrade to LightCandy@1.2.5.
|
|
||||||
*/
|
|
||||||
|
|
||||||
return self::create_cache_file( $path, $phpStr );
|
$output_folder = $this->dist_path . '/templates';
|
||||||
}
|
rename( $file_name, "$output_folder/$this->component_name.template.php" );
|
||||||
|
}
|
||||||
|
|
||||||
private static function create_cache_file( string $file_path, string $content ): string {
|
private function get_handlebars_template( $path = '' ): string {
|
||||||
$file_path_parts = explode( ".", $file_path );
|
$template = file_get_contents( $path );
|
||||||
array_pop( $file_path_parts ); // remove ".hbs" format.
|
$phpStr = LightnCandy::compile( $template,
|
||||||
$file_path_parts[] = 'php';
|
[
|
||||||
$file_path = join( '.', $file_path_parts );
|
'flags' => Flags::FLAG_NOESCAPE | Flags::FLAG_PARENT | Flags::FLAG_SPVARS | Flags::FLAG_ELSE | Flags::FLAG_JSLENGTH | Flags::FLAG_JSTRUE | Flags::FLAG_THIS,
|
||||||
|
'helpers' => $this->custom_handlebars ?? [],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$comment = "
|
/**
|
||||||
|
* NOTE:
|
||||||
|
* PHP 8.0.0 has problems with the LightCandy lib.
|
||||||
|
* If you're running the exact php8.0.0 version, try to downgrade to LightCandy@1.2.5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return self::create_cache_file( $path, $phpStr );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function create_cache_file( string $file_path, string $content ): string {
|
||||||
|
$file_path_parts = explode( ".", $file_path );
|
||||||
|
array_pop( $file_path_parts ); // remove ".hbs" format.
|
||||||
|
$file_path_parts[] = 'php';
|
||||||
|
$file_path = join( '.', $file_path_parts );
|
||||||
|
|
||||||
|
$comment = "
|
||||||
/**
|
/**
|
||||||
* FILE INFO:
|
* FILE INFO:
|
||||||
* This file was generated by LightCandy::compile function.
|
* This file was generated by LightCandy::compile function.
|
||||||
* The original source is the .HBS(handlebars) file that is located next to this generated PHP file.
|
* The original source is the .HBS(handlebars) file that is located next to this generated PHP file.
|
||||||
|
*
|
||||||
|
* Compiled at " . date( 'Y-m-d h:i:s' ) . "
|
||||||
*/
|
*/
|
||||||
|
|
||||||
";
|
";
|
||||||
|
|
||||||
$t = file_put_contents( $file_path, '<?php ' . $comment . $content . ' ?>' );
|
$t = file_put_contents( $file_path, '<?php ' . $comment . $content . ' ?>' );
|
||||||
if ( $t === false ) {
|
if ( $t === false ) {
|
||||||
die( "Error: Can't generate HBS template to PHP file. Cache folder is not accessible." );
|
throw new \Exception( "Error: Can't generate HBS template to PHP file. Cache folder is not accessible." );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $file_path;
|
return $file_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
( new Component_Builder( $argv[1], $argv[2], $argv[3] ) )->build();
|
/**
|
||||||
|
* Functions below will be triggered by JavaScript (index.js).
|
||||||
|
*/
|
||||||
|
|
||||||
|
function build( $args = [] ): void {
|
||||||
|
( new Component_Builder( $args ) )->build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ExportException
|
||||||
|
*/
|
||||||
|
function jsonToPhp( $args = [] ): ?string {
|
||||||
|
$json = $args['json'] ?? [];
|
||||||
|
|
||||||
|
return VarExporter::export( $json );
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"zordius/lightncandy": "^1.2.6"
|
"zordius/lightncandy": "^1.2.6",
|
||||||
|
"brick/varexporter": "^0.3.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+106
-1
@@ -4,8 +4,113 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "1d9b4e7a02be366b48383c185193727f",
|
"content-hash": "6731e0a19d488ee457b6682e62ef9018",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "brick/varexporter",
|
||||||
|
"version": "0.3.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/brick/varexporter.git",
|
||||||
|
"reference": "3e263cd718d242594c52963760fee2059fd5833c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/brick/varexporter/zipball/3e263cd718d242594c52963760fee2059fd5833c",
|
||||||
|
"reference": "3e263cd718d242594c52963760fee2059fd5833c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"nikic/php-parser": "^4.0",
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.2",
|
||||||
|
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||||
|
"vimeo/psalm": "4.23.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Brick\\VarExporter\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()",
|
||||||
|
"keywords": [
|
||||||
|
"var_export"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/brick/varexporter/issues",
|
||||||
|
"source": "https://github.com/brick/varexporter/tree/0.3.7"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/BenMorel",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-06-29T23:37:57+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nikic/php-parser",
|
||||||
|
"version": "v4.15.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-tokenizer": "*",
|
||||||
|
"php": ">=7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ircmaxell/php-yacc": "^0.0.7",
|
||||||
|
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/php-parse"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpParser\\": "lib/PhpParser"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nikita Popov"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A PHP parser written in PHP",
|
||||||
|
"keywords": [
|
||||||
|
"parser",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
|
||||||
|
},
|
||||||
|
"time": "2022-11-12T15:38:23+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "zordius/lightncandy",
|
"name": "zordius/lightncandy",
|
||||||
"version": "v1.2.6",
|
"version": "v1.2.6",
|
||||||
|
|||||||
@@ -1,31 +1,55 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Core\Global_Functions;
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
|
||||||
|
|
||||||
class <%= blockClassModel %>_Component {
|
class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor) { %>extends \Core\Component <% } %>{
|
||||||
|
const VERSION = '<%= version %>';
|
||||||
|
public $block_project = '<%= ownerFilename %>';
|
||||||
|
public $block_name = '<%= blockFilename %>';
|
||||||
|
public $hook_prefix = 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>';
|
||||||
|
|
||||||
public function __construct() {
|
<% if (!isComponentManager && !isElementor) { %>public function __construct() {
|
||||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] );
|
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 {
|
||||||
wp_enqueue_style( '<%= blockFilename %>',
|
$base_path = plugin_dir_url( __FILE__ ); // In Plugins
|
||||||
get_template_directory_uri() . '/components/partials/<%= blockFilename %>/templates/styles/<%= blockFilename %>.min.css',
|
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/'; // In Theme
|
||||||
[ 'style-wp' ],
|
|
||||||
Global_Functions::get_current_version_number()
|
|
||||||
);
|
|
||||||
|
|
||||||
wp_enqueue_script( '<%= blockFilename %>',
|
$style_deps = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::style_deps', [ 'assets-style' ] );
|
||||||
get_template_directory_uri() . '/components/partials/<%= blockFilename %>/templates/scripts/<%= blockFilename %>.min.js',
|
wp_register_style( 'block-<%= blockFilename %>', $base_path . 'templates/styles/<%= blockFilename %>.min.css', $style_deps, self::VERSION );
|
||||||
[ 'jquery', 'swiper' ],
|
|
||||||
Global_Functions::get_current_version_number(),
|
$script_deps = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::script_deps', [ 'assets-script' ] );
|
||||||
true
|
wp_register_script( 'block-<%= blockFilename %>', $base_path . 'templates/scripts/<%= blockFilename %>.min.js', $script_deps, self::VERSION, true );<% if (!isElementor) { %>
|
||||||
);
|
|
||||||
|
if ( ! is_admin() ) {
|
||||||
|
wp_enqueue_style( 'block-<%= blockFilename %>' );
|
||||||
|
wp_enqueue_script( 'block-<%= blockFilename %>' );
|
||||||
|
}<% } %>
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render( $args = [] ): void {
|
public function get_content( $args = [] ): string {
|
||||||
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( array_merge( [], $args ), self::class );
|
$default_args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::default_args', [] ); // Not really practical.
|
||||||
echo apply_filters( 'the_content', wpautop( $output ) );
|
|
||||||
|
$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 (isElementor) { %>
|
||||||
|
|
||||||
|
function register_custom_logic(): void {
|
||||||
|
add_action( 'elementor/widgets/widgets_registered', [ $this, 'register_elementor_widget' ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function register_elementor_widget( $widgets_manager ): void {
|
||||||
|
require_once "helpers/<%= blockClassModel %>_Elementor_Widget.php";
|
||||||
|
$widgets_manager->register_widget_type( new Helpers\<%= blockClassModel %>_Elementor_Widget() );
|
||||||
|
}<% } %>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (isComponentManager) { %><%= blockClassModel %>_Component::get_instance();<% } else {
|
||||||
|
%>new <%= blockClassModel %>_Component();<% } %>
|
||||||
|
|||||||
@@ -3,53 +3,46 @@
|
|||||||
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
|
||||||
|
|
||||||
class <%= blockClassModel %>_Component 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 %>';
|
||||||
|
|
||||||
public function get_content( $args = [] ): string {
|
public function get_content( $args = [] ): string {
|
||||||
$args = array_merge( Helpers\<%= blockClassModel %>_Defaults::default_args(), $args );
|
$default_args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::default_args', [] ); // Not really practical.
|
||||||
$args = Helpers\<%= blockClassModel %>_API::prepare_args( $args );
|
|
||||||
|
|
||||||
<% if (templateFormat === 'hbs') { %>return $this->get_component_template( __DIR__ . '/templates/<%= blockFilename %>.template.hbs', $args );<% } %>
|
$args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::prepare_args', array_merge( $default_args, $args ) );
|
||||||
<% if (templateFormat === 'php') { %>
|
|
||||||
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( array_merge( [], $args ), self::class );
|
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( $args, self::class );
|
||||||
echo apply_filters( 'the_content', wpautop( $output ) );
|
|
||||||
<% } %>
|
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
|
||||||
|
|
||||||
<% if (!include_acf_block && !include_native_gutenberg_block) { %>
|
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/';
|
||||||
public function register_assets() {
|
wp_enqueue_style( 'block-<%= blockFilename %>', $this->get_assets_path_url( 'templates/styles/<%= blockFilename %>.min.css' ), ['assets-style'], self::VERSION );<% if (include_script) { %>
|
||||||
$this->add_style( __DIR__ . '/templates/styles/<%= blockFilename %>.min.css' );
|
wp_enqueue_script( 'block-<%= blockFilename %>', $this->get_assets_path_url( 'templates/scripts/<%= blockFilename %>.min.js' ), ['assets-script'], self::VERSION, true );<% } %>
|
||||||
<% if (include_script) { %>$this->add_script( __DIR__ . '/templates/scripts/<%= blockFilename %>.js' );<% } %>
|
|
||||||
|
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' => $this->get_assets_path_url( 'templates/styles/<%= blockFilename %>.min.css' ),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'script_assets' => [
|
||||||
|
[
|
||||||
|
'name' => '<%= blockFilename %>',
|
||||||
|
'url' => $this->get_assets_path_url( 'templates/scripts/<%= blockFilename %>.min.js' ),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
<% } %><% if (include_elementor_widget) { %> function register_custom_logic(): void {
|
|
||||||
add_action( 'elementor/widgets/widgets_registered', [ $this, 'register_elementor_widget' ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
<% } %><% if (include_elementor_widget) { %> function register_elementor_widget( $widgets_manager ) {
|
|
||||||
$widgets_manager->register_widget_type( new Helpers\<%= blockClassModel %>_Elementor_Widget() );
|
|
||||||
}
|
|
||||||
|
|
||||||
<% } %><% if (include_acf_block) { %> function register_acf_block() {
|
|
||||||
$this->register_block( __DIR__ . "/<%= blockFilename %>.block.json", [
|
|
||||||
'enqueue_assets' => function () {
|
|
||||||
wp_enqueue_style( '<%= blockFilename %>', \Core\Global_Functions::get_file_url( __DIR__ . '/templates/styles/<%= blockFilename %>.min.css', true ), [], get_blocks_version() );<% if (include_script) { %>
|
|
||||||
wp_enqueue_script( '<%= blockFilename %>', \Core\Global_Functions::get_file_url( __DIR__ . '/templates/scripts/<%= blockFilename %>.js', true ), ['assets-script'], get_blocks_version() );<% } %>
|
|
||||||
},
|
|
||||||
'default' => Helpers\<%= blockClassModel %>_Defaults::default_args(),
|
|
||||||
'supports' => [
|
|
||||||
//'jsx' => true,
|
|
||||||
'color' => [
|
|
||||||
'background' => true,
|
|
||||||
'text' => true,
|
|
||||||
],
|
|
||||||
'spacing' => [
|
|
||||||
'margin' => [ 'top', 'bottom' ],
|
|
||||||
'padding' => [ 'top', 'bottom' ]
|
|
||||||
],
|
|
||||||
]
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
<% } %><% if (include_native_gutenberg_block) { %> function register_native_gutenberg_block() {
|
<% } %><% if (include_native_gutenberg_block) { %> function register_native_gutenberg_block() {
|
||||||
register_block_type( __DIR__ . '/templates/gutenberg-block/block.json' );<% if (include_script) { %>
|
register_block_type( __DIR__ . '/templates/gutenberg-block/block.json' );<% if (include_script) { %>
|
||||||
|
|
||||||
@@ -57,12 +50,13 @@ class <%= blockClassModel %>_Component extends \Core\Component {
|
|||||||
$asset_file_front = include( plugin_dir_path( __FILE__ ) . '/templates/gutenberg-block/build/front.asset.php' );
|
$asset_file_front = include( plugin_dir_path( __FILE__ ) . '/templates/gutenberg-block/build/front.asset.php' );
|
||||||
wp_enqueue_script(
|
wp_enqueue_script(
|
||||||
'gutenberg-<%= blockFilename %>-scripts-front',
|
'gutenberg-<%= blockFilename %>-scripts-front',
|
||||||
plugins_url( 'templates/gutenberg-block/build/front.js', __FILE__ ),
|
$this->get_assets_path_url( 'templates/gutenberg-block/build/front.js' ),
|
||||||
$asset_file_front['dependencies'],
|
$asset_file_front['dependencies'],
|
||||||
$asset_file_front['version'],
|
$asset_file_front['version'],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
} );<% } %>
|
} );<% } %>
|
||||||
}<% } %>
|
}<% } %>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<%= blockClassModel %>_Component::get_instance();
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
|
|||||||
|
|
||||||
class <%= blockClassModel %>_Defaults {
|
class <%= blockClassModel %>_Defaults {
|
||||||
public static function default_args(): array {
|
public static function default_args(): array {
|
||||||
return [
|
$args = <%- defaultData %>;
|
||||||
'title' => '<%= title %>',
|
|
||||||
];
|
// $args['base_url'] = \Core\Global_Functions::get_file_url( __DIR__ . '/../templates/' );
|
||||||
|
|
||||||
|
return $args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_Elementor_Widget extends \Elementor\Widget_Base {
|
||||||
|
const PLUGIN_NAME = '<%= blockFilename %>';
|
||||||
|
|
||||||
|
public function get_name() {
|
||||||
|
return self::PLUGIN_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_title() {
|
||||||
|
return '<%= blockClassModel %>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_icon() {
|
||||||
|
return 'eicon-plus-square-o';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _register_controls() {
|
||||||
|
$block_data = \Core\Block::get_block_data( __DIR__ . '/../<%= blockFilename %>.block.json' );
|
||||||
|
\Core\Elementor_Block::register_groups( $block_data, $this );
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_style_depends() {
|
||||||
|
return [ 'block-<%= blockFilename %>' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_script_depends() {
|
||||||
|
return [ 'block-<%= blockFilename %>' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render() {
|
||||||
|
$settings = $this->get_settings_for_display();
|
||||||
|
|
||||||
|
$component = new \AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\<%= blockClassModel %>_Component();
|
||||||
|
|
||||||
|
$args = self::prepare( $settings );
|
||||||
|
$component->render( $args );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function prepare( $args ): array {
|
||||||
|
// Prepare $args for render function.
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
import path from "path";
|
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 {capitalize, createFiles, getBlockName, getConfigs, readJSONFile} from "../../helpers.js";
|
||||||
import {fileURLToPath} from 'url';
|
import {fileURLToPath} from 'url';
|
||||||
|
import {copy} from "fs-extra";
|
||||||
|
import {exec} from 'child_process';
|
||||||
|
import execPhp from "exec-php";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const {projectPath} = getConfigs();
|
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'
|
||||||
|
|
||||||
export async function buildWordPress(isBlock = false) {
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
const distPath = path.join(projectPath, 'exports', 'wordpress');
|
|
||||||
await mkdir(distPath, {recursive: true})
|
|
||||||
|
|
||||||
const jsonFilePath = path.join(projectPath, 'block.json');
|
const distPath = path.join(projectPath, 'exports', args.platform);
|
||||||
await copyFile(jsonFilePath, `${distPath}/block.json`)
|
// await mkdir(distPath, {recursive: true})
|
||||||
|
await mkdir(path.join(distPath, 'templates'), {recursive: true})
|
||||||
|
|
||||||
let data = await readJSONFile(jsonFilePath);
|
const blockFilePath = path.join(projectPath, 'block.json');
|
||||||
|
|
||||||
|
let data = await readJSONFile(blockFilePath);
|
||||||
Object.assign(data, getBlockName(data.name));
|
Object.assign(data, getBlockName(data.name));
|
||||||
|
|
||||||
// let data = await readJSONFile(path.join(projectPath, `block.json`));
|
|
||||||
const title = capitalize(data.name);
|
const title = capitalize(data.name);
|
||||||
const owner = capitalize(data.project);
|
const owner = capitalize(data.project);
|
||||||
|
|
||||||
@@ -31,12 +37,71 @@ export async function buildWordPress(isBlock = false) {
|
|||||||
ownerClass: owner.replace(/ /ig, '_'),
|
ownerClass: owner.replace(/ /ig, '_'),
|
||||||
ownerFilename: owner.toLowerCase().replace(/ /ig, '-'),
|
ownerFilename: owner.toLowerCase().replace(/ /ig, '-'),
|
||||||
templateFormat: 'php',
|
templateFormat: 'php',
|
||||||
include_acf_block: false,
|
include_acf_block: isBlock,
|
||||||
include_native_gutenberg_block: false,
|
include_native_gutenberg_block: false,
|
||||||
include_script: true,
|
include_script: true,
|
||||||
include_elementor_widget: false,
|
include_elementor_widget: isElementor,
|
||||||
|
isElementor,
|
||||||
|
isComponentManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await copyFile(blockFilePath, path.join(distPath, data.blockFilename + '.block.json'));
|
||||||
|
|
||||||
|
const backPath = modulesPath ? modulesPath.split('/').map(() => '..').join('/') : '';
|
||||||
|
|
||||||
|
const phpGeneratorPath = path.join(modulesPath, 'platforms', 'php');
|
||||||
|
await execCommand(`cd ${phpGeneratorPath} && composer install`);
|
||||||
|
await execPHPFile(path.join(phpGeneratorPath, 'build.php'), 'build', {
|
||||||
|
blockName,
|
||||||
|
backPath,
|
||||||
|
projectPath,
|
||||||
|
platform: args.platform
|
||||||
|
});
|
||||||
|
|
||||||
|
await copyStaticFile(
|
||||||
|
path.join(projectPath, 'src', 'styles', `${data.blockFilename}.min.css`),
|
||||||
|
path.join(distPath, 'templates', 'styles', `${data.blockFilename}.min.css`),
|
||||||
|
);
|
||||||
|
|
||||||
|
await copyStaticFile(
|
||||||
|
path.join(projectPath, 'src', 'scripts', `${data.blockFilename}.min.js`),
|
||||||
|
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(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`,
|
||||||
|
}], {
|
||||||
|
pathDist: distPath,
|
||||||
|
generatorsPath: path.join(__dirname)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (isBlock) {
|
if (isBlock) {
|
||||||
await createFiles(data, [{
|
await createFiles(data, [{
|
||||||
from: `templates/Template_Component.php`,
|
from: `templates/Template_Component.php`,
|
||||||
@@ -46,21 +111,13 @@ export async function buildWordPress(isBlock = false) {
|
|||||||
generatorsPath: path.join(__dirname)
|
generatorsPath: path.join(__dirname)
|
||||||
});
|
});
|
||||||
|
|
||||||
await createFiles(data, [{
|
// await createFiles(data, [{
|
||||||
from: `templates/helpers/Template_API.php`,
|
// from: `templates/helpers/Template_API.php`,
|
||||||
to: `helpers/${data.blockClassModel}_API.php`,
|
// to: `helpers/${data.blockClassModel}_API.php`,
|
||||||
}], {
|
// }], {
|
||||||
pathDist: distPath,
|
// pathDist: distPath,
|
||||||
generatorsPath: path.join(__dirname)
|
// generatorsPath: path.join(__dirname)
|
||||||
});
|
// });
|
||||||
|
|
||||||
await createFiles(data, [{
|
|
||||||
from: `templates/helpers/Template_Defaults.php`,
|
|
||||||
to: `helpers/${data.blockClassModel}_Defaults.php`,
|
|
||||||
}], {
|
|
||||||
pathDist: distPath,
|
|
||||||
generatorsPath: path.join(__dirname)
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
await createFiles(data, [{
|
await createFiles(data, [{
|
||||||
from: `templates/Template_Basic_Component.php`,
|
from: `templates/Template_Basic_Component.php`,
|
||||||
@@ -71,3 +128,44 @@ export async function buildWordPress(isBlock = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function execCommand(cmd = '') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
exec(cmd, function (error) {
|
||||||
|
if (error) {
|
||||||
|
console.log('Error:', error)
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(stdout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function execPHPFile(file = '', functionName = '', args = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execPhp(file, 'php', (err, php, out) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(out);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
php[functionName.toLowerCase()](args, (err, res, out) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(out);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(res);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyStaticFile(from = '', to = '') {
|
||||||
|
await mkdir(path.dirname(to), {recursive: true})
|
||||||
|
await copyFile(from, to);
|
||||||
|
}
|
||||||
|
|||||||
+42
-5
@@ -3,13 +3,17 @@ import {nodeResolve} from '@rollup/plugin-node-resolve';
|
|||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
import css from "@modular-css/rollup";
|
import css from "@modular-css/rollup";
|
||||||
import copy from 'rollup-plugin-copy'
|
import copy from 'rollup-plugin-copy';
|
||||||
|
import {terser} from "rollup-plugin-terser";
|
||||||
|
|
||||||
export default {
|
const devMode = (process.env.NODE_ENV === 'development');
|
||||||
|
console.log('Build Mode', devMode ? 'Development' : 'Production');
|
||||||
|
|
||||||
|
export default [{
|
||||||
input: 'layouts/scripts/index.js',
|
input: 'layouts/scripts/index.js',
|
||||||
output: {
|
output: {
|
||||||
file: 'layouts/scripts/dist/index.min.js',
|
file: 'layouts/scripts/dist/index.min.js',
|
||||||
sourcemap: true
|
sourcemap: devMode,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve({
|
nodeResolve({
|
||||||
@@ -26,10 +30,43 @@ export default {
|
|||||||
presets: ["@babel/preset-react"],
|
presets: ["@babel/preset-react"],
|
||||||
}),
|
}),
|
||||||
commonjs(),
|
commonjs(),
|
||||||
|
!devMode && terser(),
|
||||||
copy({
|
copy({
|
||||||
targets: [
|
targets: [
|
||||||
{ src: 'layouts/scripts/toolbar/images/**/*', dest: 'layouts/scripts/dist/toolbar/images' }
|
{src: 'layouts/scripts/toolbar/images/**/*', dest: 'layouts/scripts/dist/toolbar/images'}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
}
|
}, {
|
||||||
|
input: 'layouts/scripts/frame/frame.js',
|
||||||
|
output: {
|
||||||
|
file: 'layouts/scripts/dist/frame-index.min.js',
|
||||||
|
sourcemap: devMode
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
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()
|
||||||
|
]
|
||||||
|
}];
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {getBlockConfigs, getBlockName, getBlockVariations, handlebarLayoutsPath} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
import {buildAssetFiles} from "../inc/changes-watcher.js";
|
||||||
|
|
||||||
|
export default async function (req, res) {
|
||||||
|
const blockName = req.query.block;
|
||||||
|
const block = getBlockName(blockName);
|
||||||
|
|
||||||
|
const blockDir = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const dataFiles = await getBlockVariations(blockDir);
|
||||||
|
|
||||||
|
const data = await getDataOfFrame(!!req.query.iframe, Object.assign({}, block, {dataFiles}));
|
||||||
|
|
||||||
|
if (data.error && data.errorMessage) {
|
||||||
|
return res.send(data.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseView = req.params.baseView ?? 'alignfull';
|
||||||
|
|
||||||
|
await buildAssetFiles(blockDir);
|
||||||
|
|
||||||
|
res.render(baseView, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDataOfFrame(isIframe = false, block) {
|
||||||
|
const data = {config: await getBlockConfigs(block)};
|
||||||
|
if (data.error && data.errorMessage) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.helpers = {
|
||||||
|
include_partial: (filesPath) => handlebarLayoutsPath(filesPath),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIframe) {
|
||||||
|
data.iframeMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.staticFilesPath = `/block/${block.project}/${block.name}`;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import {
|
||||||
|
getBlockData,
|
||||||
|
getBlockName,
|
||||||
|
getBlockVariations,
|
||||||
|
getListOfDesignPreviewFiles,
|
||||||
|
} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
|
||||||
|
export default async function (req, res, next) {
|
||||||
|
let variationName = req.query.name ? req.query.name : 'default';
|
||||||
|
const block = getBlockName(req.query.block);
|
||||||
|
|
||||||
|
const blockDir = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const dataFiles = await getBlockVariations(blockDir);
|
||||||
|
const data = await getBlockData(variationName, {projectPath: blockDir});
|
||||||
|
|
||||||
|
let designPreviewFiles = [];
|
||||||
|
try {
|
||||||
|
designPreviewFiles = getListOfDesignPreviewFiles(variationName, await fs.readdir(path.join(blockDir, 'design', 'preview')), block);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Preview Design doesn\'t exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
dataOptions: dataFiles,
|
||||||
|
designPreview: designPreviewFiles,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import {getBlockConfigs, getBlockName, getBlockVariations, handlebarLayoutsPath, verifyVersion} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
import {REGISTRY_URL} from "@axe-web/create-block/env.js";
|
||||||
|
|
||||||
|
export async function index(req, res, next) {
|
||||||
|
|
||||||
|
const blockName = req.query.block;
|
||||||
|
const block = getBlockName(blockName);
|
||||||
|
|
||||||
|
const blockDir = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const dataFiles = await getBlockVariations(blockDir);
|
||||||
|
|
||||||
|
const data = await getBlockConfigs(Object.assign({}, block, {dataFiles}));
|
||||||
|
|
||||||
|
if (data.error && data.errorMessage) {
|
||||||
|
// TODO: Throw Error.
|
||||||
|
}
|
||||||
|
|
||||||
|
data.helpers = {
|
||||||
|
include_partial: (filesPath) => handlebarLayoutsPath('.', filesPath),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make sure we sync block before we start working on it.
|
||||||
|
try {
|
||||||
|
const verifiedVersion = await verifyVersion(blockName, blockDir, REGISTRY_URL);
|
||||||
|
if (!verifiedVersion && !req.query['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.blockName = blockName;
|
||||||
|
data.baseView = 'alignfull';
|
||||||
|
data.port = `/view/${data.baseView}`;
|
||||||
|
|
||||||
|
let previewFrameUrl = ``; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
|
||||||
|
data.previewFrameUrl = `${previewFrameUrl}${data.port}`;
|
||||||
|
data.previewFrameUrlNewWindow = `${previewFrameUrl}${data.port}`;
|
||||||
|
data.shareUrl = '';
|
||||||
|
|
||||||
|
data.viewMode = process.env.VIEW_MODE ?? false;
|
||||||
|
|
||||||
|
res.render('index', data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default async function (req, res, next) {
|
||||||
|
|
||||||
|
if (process.env.VIEW_MODE) {
|
||||||
|
throw new Error("Can't publish in view mode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export function staticFiles(req, res) {
|
||||||
|
const fileName = req.params[0];
|
||||||
|
const path = `blocks/@${req.params.project}/${req.params.blockName}/src/${fileName}`;
|
||||||
|
|
||||||
|
return deliverStaticFile(path, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function previewFiles(req, res) {
|
||||||
|
const fileName = req.params[0];
|
||||||
|
const path = `blocks/@${req.params.project}/${req.params.blockName}/design/${fileName}`;
|
||||||
|
|
||||||
|
return deliverStaticFile(path, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deliverStaticFile(path, res) {
|
||||||
|
// If file doesn't exist, return 404.
|
||||||
|
res.sendFile(path, {root: './'}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
res.send('File not found.');
|
||||||
|
res.status(err.status).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import {syncFilesWithCloud} from "../helpers.js";
|
||||||
|
|
||||||
|
export default async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
await syncFilesWithCloud(req.body.block, null, true);
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(500).json({status: 500, message: err.message});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({status: 200, message: 'Successfully synced!'});
|
||||||
|
}
|
||||||
@@ -1,396 +1,35 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import path from 'path';
|
import {init} from "./inc/server.js";
|
||||||
import fetch from "node-fetch";
|
import {index} from "./routes/index.js";
|
||||||
import express from 'express';
|
import dataRoute from "./routes/data.js";
|
||||||
import {create} from 'express-handlebars';
|
import baseView from "./routes/base-view.js";
|
||||||
import fsExtra from 'fs-extra';
|
import syncRoute from "./routes/sync.js";
|
||||||
import browserSync from 'browser-sync';
|
import publishRoute from "./routes/publish.js";
|
||||||
import config from 'config';
|
import {ViewSync} from "./inc/view-sync.js";
|
||||||
import gulp from 'gulp';
|
import {previewFiles, staticFiles} from "./routes/static-files.js";
|
||||||
import babel from "gulp-babel";
|
import {setupWatcher} from "./inc/changes-watcher.js";
|
||||||
import uglify from "gulp-uglify";
|
|
||||||
import rename from "gulp-rename";
|
|
||||||
import dartSass from 'sass';
|
|
||||||
import gulpSass from 'gulp-sass';
|
|
||||||
import sourcemaps from "gulp-sourcemaps";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import open from "open";
|
|
||||||
import {sanitizeUrl} from "@braintree/sanitize-url";
|
|
||||||
import sanitizeHtml from 'sanitize-html';
|
|
||||||
import {escape} from "lodash-es";
|
|
||||||
import archiver from 'archiver';
|
|
||||||
import {getBlockConfigs, getConfigs, readJSONFile} from "./helpers.js";
|
|
||||||
import PluginError from 'plugin-error';
|
|
||||||
|
|
||||||
/**
|
const {expressApp, httpServer} = init();
|
||||||
* Constants
|
|
||||||
*/
|
|
||||||
|
|
||||||
const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs();
|
const viewSync = new ViewSync(httpServer);
|
||||||
const blocksRegistry = isDev ? 'http://localhost:3020' : 'https://axe-web-blocks-registry.captain.devdevdev.life';
|
setupWatcher(viewSync);
|
||||||
|
|
||||||
/**
|
const PORT = process.env.PORT || 3010;
|
||||||
* Init server
|
|
||||||
*/
|
|
||||||
|
|
||||||
let port = 3000; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
|
httpServer.listen(PORT, '0.0.0.0', () => {
|
||||||
let previewFrameUrl = `http://localhost:${port}`; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
|
console.log(`Server app listening on port ${PORT}`)
|
||||||
const dataFiles = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
|
|
||||||
const app = express();
|
|
||||||
const sass = gulpSass(dartSass);
|
|
||||||
|
|
||||||
const hbs = create({
|
|
||||||
extname: '.hbs', defaultLayout: false, partialsDir: ['.'], helpers: {
|
|
||||||
esc_attr(attr) {
|
|
||||||
return escape(attr);
|
|
||||||
}, esc_url(url) {
|
|
||||||
return sanitizeUrl(url);
|
|
||||||
}, esc_html(html) {
|
|
||||||
// TODO: Check if we can remove this helper.
|
|
||||||
return html;
|
|
||||||
}, safe_html(html) {
|
|
||||||
return sanitizeHtml(html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.engine('.hbs', hbs.engine);
|
// Middleware
|
||||||
app.set('view engine', '.hbs');
|
// expressApp.use(setHeaders);
|
||||||
app.set('views', path.join(modulesPath, 'layouts'));
|
|
||||||
|
|
||||||
//
|
|
||||||
// Routes
|
// Routes
|
||||||
//
|
expressApp.get('/', index);
|
||||||
|
expressApp.get('/data', dataRoute);
|
||||||
|
expressApp.get('/view/:baseView', baseView);
|
||||||
|
expressApp.get('/design/:project/:blockName/*', previewFiles);
|
||||||
|
expressApp.get('/block/:project/:blockName/*', staticFiles);
|
||||||
|
expressApp.post('/sync', syncRoute);
|
||||||
|
expressApp.get('/publish', publishRoute);
|
||||||
|
|
||||||
app.get('/', async (req, res) => {
|
|
||||||
let jsonFileName = req.query.data ? req.query.data : 'default';
|
|
||||||
const data = await getBlockConfigs(jsonFileName, {includeConfigs: true, projectPath, modulesPath, dataFiles});
|
|
||||||
if (data.error && data.errorMessage) {
|
|
||||||
return res.send(data.errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseView = config.has('baseView') ? config.get('baseView') : 'container';
|
|
||||||
const baseViewUrl = `view/${baseView}`;
|
|
||||||
|
|
||||||
data.helpers = {
|
|
||||||
port,
|
|
||||||
include_partial: (filesPath) => path.join(modulesPath, filesPath),
|
|
||||||
baseView,
|
|
||||||
previewFrameUrl: `${previewFrameUrl}/${baseViewUrl}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render('index', data);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/view/:baseView', async (req, res) => {
|
|
||||||
let jsonFileName = req.query.data ? req.query.data : 'default';
|
|
||||||
const data = await getBlockConfigs(jsonFileName, {includeConfigs: true, projectPath, modulesPath, dataFiles});
|
|
||||||
if (data.error && data.errorMessage) {
|
|
||||||
return res.send(data.errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const blockName = config.has('blockName') ? config.get('blockName') : developmentBlockName;
|
|
||||||
|
|
||||||
data.helpers = {
|
|
||||||
include_partial: (filesPath) => path.join(modulesPath, filesPath),
|
|
||||||
include_block_template: () => path.join(projectPath, 'src', `${blockName}.template`),
|
|
||||||
section_class: `${blockName}--${jsonFileName}`,
|
|
||||||
base_url: '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseView = req.params.baseView ?? 'container';
|
|
||||||
|
|
||||||
res.render(baseView, data)
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/publish', async (req, res) => {
|
|
||||||
const data = await readJSONFile(path.join(projectPath, `block.json`));
|
|
||||||
let responseData;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${blocksRegistry}`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
headers: {'Content-Type': 'application/json'}
|
|
||||||
});
|
|
||||||
|
|
||||||
responseData = await response.json();
|
|
||||||
} catch (e) {
|
|
||||||
res.json({success: false, message: 'Blocks Registry server is not available.'});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseData.statusCode !== 200) {
|
|
||||||
res.json({success: false, message: 'Error on registry level.'});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseData.uploadUrl) {
|
|
||||||
await zipProject();
|
|
||||||
const body = await fs.readFile(path.join(projectPath, 'dist.zip'));
|
|
||||||
const response = await fetch(`${responseData.uploadUrl}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body,
|
|
||||||
headers: {'Content-Type': 'application/zip'}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
|
||||||
res.json({success: false, message: "Can't upload the archive, permissions error."});
|
|
||||||
// TODO: Need to update the registry server.
|
|
||||||
await fs.unlink(path.join(projectPath, 'dist.zip'));
|
|
||||||
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 data = await getBlockConfigs(jsonDataFileName, {projectPath, modulesPath, dataFiles});
|
|
||||||
const designPreviewFiles = getListOfDesignPreviewFiles(jsonDataFileName, await fs.readdir(path.join(projectPath, 'design', 'preview')));
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
dataOptions: dataFiles,
|
|
||||||
designPreview: designPreviewFiles,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Errors handler
|
|
||||||
app.use(handleSyntaxErrors);
|
|
||||||
|
|
||||||
// Static Files
|
|
||||||
app.use(express.static(path.join(projectPath, 'src')));
|
|
||||||
app.use(express.static(path.join(projectPath, 'design')));
|
|
||||||
app.use(express.static(path.join(modulesPath, 'layouts')));
|
|
||||||
|
|
||||||
// Setup Gulp
|
|
||||||
await buildAssetFiles();
|
|
||||||
|
|
||||||
// BrowserSync
|
|
||||||
const bsOptions = await startBrowserSync();
|
|
||||||
port = bsOptions.port;
|
|
||||||
previewFrameUrl = bsOptions.previewFrameUrl;
|
|
||||||
await open(bsOptions.devToolUrl);
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Functions
|
|
||||||
//
|
|
||||||
|
|
||||||
function getListOfDesignPreviewFiles(jsonDataFileName, previewFiles) {
|
|
||||||
return previewFiles
|
|
||||||
.filter(fileName => {
|
|
||||||
return fileName.startsWith(jsonDataFileName + '.');
|
|
||||||
})
|
|
||||||
.map(fileName => {
|
|
||||||
const fileData = fileName.split('.');
|
|
||||||
const fileFormat = fileData.pop();
|
|
||||||
const previewSize = fileData.pop();
|
|
||||||
|
|
||||||
return {
|
|
||||||
dataSource: jsonDataFileName,
|
|
||||||
widthDimension: Number.parseInt(previewSize, 10),
|
|
||||||
url: `/preview/${fileName}`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function startBrowserSync() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const listener = app.listen(0, async () => {
|
|
||||||
const PORT = listener.address().port;
|
|
||||||
|
|
||||||
console.log(`The web server has started on port ${PORT}`);
|
|
||||||
|
|
||||||
const bs = browserSync.create();
|
|
||||||
|
|
||||||
const files = getJSBundleFiles();
|
|
||||||
gulp.watch(files, {delay: 400}, gulp.series(['build-script-files', function (cb) {
|
|
||||||
browserSyncReload(bs, 'js', 'Script Files Change');
|
|
||||||
return cb();
|
|
||||||
}]));
|
|
||||||
|
|
||||||
gulp.watch(path.join(projectPath, 'src/**/*.scss'), {delay: 400}, gulp.series(['build-styling-files', function (cb) {
|
|
||||||
browserSyncReload(bs, 'css', 'Style Files Change');
|
|
||||||
return cb();
|
|
||||||
}]));
|
|
||||||
|
|
||||||
bs.watch("src/**/*.hbs", function (event, file) {
|
|
||||||
browserSyncReload(bs, '', 'Template File Change: ' + file)
|
|
||||||
});
|
|
||||||
|
|
||||||
bs.init({
|
|
||||||
proxy: `http://localhost:${PORT}`,
|
|
||||||
open: false
|
|
||||||
}, (err, bs) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = bs.getOptions().toJS();
|
|
||||||
|
|
||||||
const urls = {
|
|
||||||
devTool: options.urls.local.replace(options.port, options.proxy.url.port),
|
|
||||||
previewFrame: options.urls.local,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If local network is available.
|
|
||||||
if (options.urls.external) {
|
|
||||||
urls.devTool = options.urls.external.replace(options.port, options.proxy.url.port);
|
|
||||||
urls.previewFrame = options.urls.external;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
devToolUrl: urls.devTool,
|
|
||||||
previewFrameUrl: urls.previewFrame,
|
|
||||||
port: options.port
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function browserSyncReload(bs, extension = '', message = '') {
|
|
||||||
if (isDev) {
|
|
||||||
// console.log(event, file);
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extension) {
|
|
||||||
extension = "*." + extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs.reload(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getJSBundleFiles() {
|
|
||||||
return [path.join(projectPath, 'src/**/*.js'), path.join(projectPath, 'src/**/*.mjs'), '!' + path.join(projectPath, 'src/**/*.min.js')];
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildScriptFiles(done) {
|
|
||||||
const files = getJSBundleFiles();
|
|
||||||
return gulp.src(files)
|
|
||||||
.pipe(sourcemaps.init({}))
|
|
||||||
.pipe(babel()).on('error', function (error) {
|
|
||||||
showError(new PluginError('JavaScript', error).toString());
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.pipe(gulp.src(path.join(projectPath, 'vendor/*.js')))
|
|
||||||
// .pipe(gulp.dest('src/'))
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(rename({extname: '.min.js'}))
|
|
||||||
.pipe(sourcemaps.write('.'))
|
|
||||||
.pipe(gulp.dest(path.join(projectPath, 'src/')));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildStyleFiles(done) {
|
|
||||||
return gulp.src(path.join(projectPath, 'src/**/*.scss'))
|
|
||||||
.pipe(sourcemaps.init({}))
|
|
||||||
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', function (error) {
|
|
||||||
showError(new PluginError('SCSS', error.messageFormatted).toString());
|
|
||||||
// sass.logError(error);
|
|
||||||
done();
|
|
||||||
}))
|
|
||||||
// .pipe(gulp.dest('src/'))
|
|
||||||
.pipe(rename({extname: '.min.css'}))
|
|
||||||
.pipe(sourcemaps.write('.', {}))
|
|
||||||
.pipe(gulp.dest(path.join(projectPath, 'src')))
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAssetFiles() {
|
|
||||||
// Register tasks.
|
|
||||||
gulp.task('build-script-files', buildScriptFiles);
|
|
||||||
gulp.task('build-styling-files', buildStyleFiles);
|
|
||||||
|
|
||||||
// Run first build.
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
gulp.series('build-script-files', 'build-styling-files', function (cb) {
|
|
||||||
resolve();
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(errorMessage) {
|
|
||||||
console.log(errorMessage);
|
|
||||||
|
|
||||||
// TODO: Send this message to browser.
|
|
||||||
// So the developer can understand there is an error.
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareListOfDataFiles(dataFiles) {
|
|
||||||
return dataFiles
|
|
||||||
.filter((fileName) => fileName.split('.').pop() === 'json')
|
|
||||||
.map((fileName) => {
|
|
||||||
const splitName = fileName.split('.');
|
|
||||||
splitName.pop();
|
|
||||||
return splitName.join('');
|
|
||||||
})
|
|
||||||
.sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function zipProject() {
|
|
||||||
// create a file to stream archive data to.
|
|
||||||
const output = await fsExtra.createWriteStream('dist.zip');
|
|
||||||
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.');
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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
|
|
||||||
archive.on('error', function (err) {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// pipe archive data to the file
|
|
||||||
archive.pipe(output);
|
|
||||||
|
|
||||||
// append files from a subdirectory, putting its contents at the root of archive
|
|
||||||
archive.directory(path.join(projectPath, 'src', '/'), 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSyntaxErrors(err, req, res, next) {
|
|
||||||
if (err) {
|
|
||||||
return res.render('error', {
|
|
||||||
helpers: {
|
|
||||||
include_partial: (filesPath) => path.join(modulesPath, filesPath),
|
|
||||||
},
|
|
||||||
err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* FUTURE TESTS.
|
||||||
|
*
|
||||||
|
* # ENV
|
||||||
|
* In `blocks-builder` service, we update MODULE_PATH and PROJECT_PATH environment variables before we run platform
|
||||||
|
* bundle build process. Actually before we call buildExportFiles().
|
||||||
|
*
|
||||||
|
* We have to make sure that this logic is working properly and stable.
|
||||||
|
*
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user