+ try {
+ html = template(jsonData);
+ } catch (e) {
+ html = `
Syntax Error:
${e.toString()}
`;
- }
+ }
- target.innerHTML = html;
- if (reload) {
- reload();
+ target.innerHTML = html;
+ if (reload) {
+ reload();
+ }
}
-}
+})();
diff --git a/layouts/scripts/sync.jsx b/layouts/scripts/sync.jsx
index 869cc06..94c18f6 100644
--- a/layouts/scripts/sync.jsx
+++ b/layouts/scripts/sync.jsx
@@ -46,17 +46,25 @@ function SyncScreen() {
>
async function ignoreVersionSync() {
- setLoading(true);
+ // 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({ignore: true}),
+ body: JSON.stringify({block: getQueryParams().block}),
headers: {
'Content-Type': 'application/json'
}
});
+
data = await response.json();
} catch (err) {
setError('Error: ' + err.message);
@@ -73,28 +81,21 @@ function SyncScreen() {
setTimeout(() => window.location.reload(), 1000);
}
- async function runSyncLogic() {
- setLoading('syncing');
+ // 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 = {};
- let data = {};
- try {
- const response = await fetch('/sync', {method: 'POST'});
- data = await response.json();
- } catch (err) {
- setError('Error: ' + err.message);
- setLoading(false);
- return;
+ for (const [key, value] of urlParams) {
+ params[key] = value;
}
- if (data.status !== 200) {
- setError(data.message);
- setLoading(false);
- return;
- }
-
- setTimeout(() => window.location.reload(), 1000);
+ return params;
}
+
}
init();
+
diff --git a/layouts/scripts/toolbar/data-options/DataOptions.jsx b/layouts/scripts/toolbar/data-options/DataOptions.jsx
index 91f9191..d8e717b 100644
--- a/layouts/scripts/toolbar/data-options/DataOptions.jsx
+++ b/layouts/scripts/toolbar/data-options/DataOptions.jsx
@@ -169,7 +169,7 @@ function DataOptions(props = {}) {
}
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}`);
return await response.json();
}
diff --git a/layouts/scripts/toolbar/publish.jsx b/layouts/scripts/toolbar/publish.jsx
index 6470343..4bae06e 100644
--- a/layouts/scripts/toolbar/publish.jsx
+++ b/layouts/scripts/toolbar/publish.jsx
@@ -43,6 +43,7 @@ function Publish(props = {}) {
export function setupPublish(rootAttributes) {
// INIT
const wrapper = document.createElement('div');
+ wrapper.setAttribute('id', 'publish-btn');
document.querySelector('.page_toolbar__right').append(wrapper)
const root = ReactDOM.createRoot(wrapper);
diff --git a/layouts/styles/page--main.css b/layouts/styles/page--main.css
index ac856e0..fbe1aa4 100644
--- a/layouts/styles/page--main.css
+++ b/layouts/styles/page--main.css
@@ -8,6 +8,9 @@ body {
font-size: 1rem;
background-color: #F7FAFC;
}
+body.view-mode #publish-btn {
+ display: none;
+}
.btn {
--btn-color: #333;
diff --git a/layouts/styles/page--main.css.map b/layouts/styles/page--main.css.map
index a3de447..503b47b 100644
--- a/layouts/styles/page--main.css.map
+++ b/layouts/styles/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;;;AAGF;EACE;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EACA;EACA;;AAEA;EACE;EAEA;EACA;;;AHiEJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;;;AAGF;EAEE;EACA","file":"page--main.css"}
\ No newline at end of file
+{"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"}
\ No newline at end of file
diff --git a/layouts/styles/page--main.scss b/layouts/styles/page--main.scss
index 1a07ba2..9ab0f15 100644
--- a/layouts/styles/page--main.scss
+++ b/layouts/styles/page--main.scss
@@ -8,6 +8,12 @@ body {
font-size: 1rem;
//overflow: none;
background-color: #F7FAFC;
+
+ &.view-mode {
+ #publish-btn {
+ display: none;
+ }
+ }
}
@import "buttons";
diff --git a/package.json b/package.json
index a689fce..0a1c099 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,9 @@
"url": "https://axe-web.com/"
},
"scripts": {
- "info": "BLOCK_NAME=swiper-test MODULE_PATH= node debug.js",
- "dev": "BLOCK_NAME=swiper-test MODULE_PATH= node server.js",
+ "info": "node debug.js",
+ "dev": "node server.js",
+ "view-mode": "VIEW_MODE=true node server.js",
"build-platform": "BLOCK_NAME=swiper-test MODULE_PATH= node ./build.js",
"dev-dev-tool": "rollup --config rollup.config.js --watch",
"build-dev-tool": "rollup --config rollup.config.js"
diff --git a/routes/base-view.js b/routes/base-view.js
new file mode 100644
index 0000000..77d1ae6
--- /dev/null
+++ b/routes/base-view.js
@@ -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;
+}
+
diff --git a/routes/data.js b/routes/data.js
new file mode 100644
index 0000000..2d6fe74
--- /dev/null
+++ b/routes/data.js
@@ -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,
+ });
+}
diff --git a/routes/index.js b/routes/index.js
new file mode 100644
index 0000000..26934d9
--- /dev/null
+++ b/routes/index.js
@@ -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);
+}
diff --git a/routes/publish.js b/routes/publish.js
new file mode 100644
index 0000000..adc5440
--- /dev/null
+++ b/routes/publish.js
@@ -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.");
+ }
+
+}
diff --git a/routes/static-files.js b/routes/static-files.js
new file mode 100644
index 0000000..e6ce353
--- /dev/null
+++ b/routes/static-files.js
@@ -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();
+ }
+ });
+}
diff --git a/routes/sync.js b/routes/sync.js
new file mode 100644
index 0000000..cdb1ff6
--- /dev/null
+++ b/routes/sync.js
@@ -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!'});
+}
diff --git a/server.js b/server.js
index ec16e14..4c87627 100755
--- a/server.js
+++ b/server.js
@@ -1,604 +1,32 @@
#!/usr/bin/env node
-import {PRODUCTION_REGISTRY_URL} from "./env.js";
-import path from 'path';
-import fetch from "node-fetch";
-import express from 'express';
-import {create} from 'express-handlebars';
-import browserSync from 'browser-sync';
-import config from 'config';
-import gulp from 'gulp';
-import babel from "gulp-babel";
-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 {
- getBlockConfigs,
- getBlockData,
- getBlockName,
- getConfigs,
- getImagesList,
- isFileEmpty,
- readJSONFile,
- removeCommentsFromCss,
- removeCommentsFromJs,
- replaceNames,
- syncFilesWithCloud,
- uploadFile,
- verifyVersion,
- zipProject
-} from "./helpers.js";
-import PluginError from 'plugin-error';
-import {Server} from "socket.io";
-import {createServer} from 'http';
-import {authtoken, connect} from "ngrok";
-import bodyParser from "body-parser";
+import {init} from "./inc/server.js";
+import {index} from "./routes/index.js";
+import dataRoute from "./routes/data.js";
+import baseView from "./routes/base-view.js";
+import syncRoute from "./routes/sync.js";
+import publishRoute from "./routes/publish.js";
+import {ViewSync} from "./inc/view-sync.js";
+import {previewFiles, staticFiles} from "./routes/static-files.js";
+import {setupWatcher} from "./inc/changes-watcher.js";
-/**
- * Constants
- */
+const {expressApp, httpServer} = init();
-const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs();
-const blocksRegistry = isDev ? 'http://localhost:3020' : PRODUCTION_REGISTRY_URL;
-const DevToolToken = 'D9lgz0TvzXCnp0xnwVBL109DaAR6Puk6F7YewDhgmP8='; // Temporary token for development purposes.
+const viewSync = new ViewSync(httpServer);
+setupWatcher(viewSync);
-const dataFiles = await getDataFiles(projectPath);
-const DEFAULT_VIEW_LAYOUT = 'alignfull';
+const PORT = process.env.PORT || 3010;
-/**
- * State
- */
-
-let port = 3000; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
-let previewFrameUrl = `/`; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
-let shareUrl = '';
-const sessions = [];
-let ignoreVersionSync = false;
-let bs;
-
-/**
- * Init server
- */
-
-const app = express();
-
-// parse application/x-www-form-urlencoded
-app.use(bodyParser.urlencoded({extended: false}))
-
-// parse application/json
-app.use(bodyParser.json())
-
-const httpServer = createServer(app);
-initSessionsServer(httpServer);
-
-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);
- }
- }
+httpServer.listen(PORT, '0.0.0.0', () => {
+ console.log(`Server app listening on port ${PORT}`)
});
-app.engine('.hbs', hbs.engine);
-app.set('view engine', '.hbs');
-app.set('views', path.join(modulesPath, 'layouts'));
-
-//
-// Routes
-//
-
-app.get('/', async (req, res, next) => {
- const data = getBlockConfigs({modulesPath, dataFiles});
- if (data.error && data.errorMessage) {
- return res.send(data.errorMessage);
- }
-
- const baseView = config.has('baseView') ? config.get('baseView') : DEFAULT_VIEW_LAYOUT;
- const baseViewUrl = `view/${baseView}`;
-
- data.helpers = {
- include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath),
- }
-
- try {
- const verifiedVersion = await verifyVersion(projectPath, blocksRegistry);
- if (!verifiedVersion && !ignoreVersionSync) {
- return res.render('sync', data);
- }
- } catch (err) {
- const errorMessage = "Can't verify block version.";
- console.log(errorMessage, err);
- return next(new Error(errorMessage));
- }
-
- data.baseView = baseView;
- data.port = `/${baseViewUrl}`;
- data.previewFrameUrl = `${previewFrameUrl}/${baseViewUrl}`;
- data.shareUrl = shareUrl;
-
- // TODO: Need to review this logic, conflicts with the browsersync work after "/sync" action.
- // if (req.headers.referer) {
- // // NGROK, public URL
- // data.shareUrl = undefined; // Link already shared.
- // data.previewFrameUrl = `/${baseViewUrl}`;
- // data.publicUrl = true;
- // }
-
- res.render('index', data);
-});
-
-app.get('/view/:baseView', (req, res) => {
- const data = getDataOfFrame(!!req.query.iframe);
-
- if (data.error && data.errorMessage) {
- return res.send(data.errorMessage);
- }
-
- const baseView = req.params.baseView ?? DEFAULT_VIEW_LAYOUT;
-
- res.render(baseView, data)
-});
-
-app.get('/publish', async (req, res) => {
- const data = await readJSONFile(path.join(projectPath, `block.json`));
-
- // Trigger build on the registry server only if the type of the unit is `foundation` or `component`.
- const uploadStaticFiles = ['foundation', 'component'].includes(data.type);
-
- // Prepare list of static files for the registry server.
- if (uploadStaticFiles) {
- data.static_files = {
- css: getBlockName(data.name).name + '.min.css',
- js: getBlockName(data.name).name + '.min.js',
- images: await getImagesList(path.join(projectPath, 'src', 'images')),
- }
-
- if (await isFileEmpty(path.join(projectPath, `src/scripts`, data.static_files.js), true)) {
- delete data.static_files.js;
- }
-
- if (!data.static_files.images.length) {
- delete data.static_files.images;
- }
- }
-
- let responseData = {
- uploadBundleUrl: undefined,
- staticFilesUrls: {}
- };
-
- 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;
- }
-
- // Start files uploading process.
- try {
- if (responseData.uploadBundleUrl) {
- await zipProject(path.join(projectPath, 'src'), path.join(projectPath, 'dist.zip'));
- await uploadFile(path.join(projectPath, 'dist.zip'), responseData.uploadBundleUrl); // Bundle
- }
-
- if (uploadStaticFiles) {
- if (responseData.staticFilesUrls.css) {
- await uploadFile(
- path.join(projectPath, 'src/styles', data.static_files.css),
- responseData.staticFilesUrls.css.uploadUrl,
- (content) => {
- if (responseData.staticFilesUrls.images) {
- content = replaceNames(content, data.static_files.images, responseData.staticFilesUrls.images);
- }
-
- removeCommentsFromCss(content)
- return content;
- }); // CSS
- }
-
- if (responseData.staticFilesUrls.js) {
- await uploadFile(
- path.join(projectPath, 'src/scripts', data.static_files.js),
- responseData.staticFilesUrls.js.uploadUrl,
- (data) => removeCommentsFromJs(data)
- ); // JS
- }
-
- if (responseData.staticFilesUrls.images) {
- for (let i = 0; i < data.static_files.images.length; i++) {
- await uploadFile(
- path.join(projectPath, 'src/images', data.static_files.images[i]),
- responseData.staticFilesUrls.images[i].uploadUrl,
- ); // Images
- }
- }
- }
- } catch (err) {
- // TODO: Need to update the registry server.
- await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
-
- res.json({success: false, message: err.message});
- return;
- }
-
- await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
-
- // Trigger project's global files build on the registry server.
- if (uploadStaticFiles) {
- try {
- await triggerGlobalProjectFilesBuild(getBlockName(data.name).project);
- } catch (err) {
- res.json({success: false, message: 'Something wrong with Project Builder.'});
- return;
- }
- }
-
- try {
- await syncFilesWithCloud(path.join(projectPath, 'block.json'), bs);
- } catch (err) {
- res.json({success: false, message: err});
- return;
- }
-
- res.json({success: true});
-});
-
-app.get('/data', async (req, res) => {
- let jsonDataFileName = req.query.name ? req.query.name : 'default';
-
- const dataFiles = await getDataFiles(projectPath);
- const data = await getBlockData(jsonDataFileName, {projectPath});
-
- let designPreviewFiles = [];
- try {
- designPreviewFiles = getListOfDesignPreviewFiles(jsonDataFileName, await fs.readdir(path.join(projectPath, 'design', 'preview')));
- } catch (err) {
- console.log('Preview Design doesn\'t exist');
- }
-
- return res.json({
- dataOptions: dataFiles,
- designPreview: designPreviewFiles,
- data,
- });
-});
-
-app.post('/sync', async (req, res) => {
- if (req.body['ignore']) {
- ignoreVersionSync = true;
- res.json({status: 200, message: 'Version upgrade is ignored.'});
- return;
- }
-
- try {
- await syncFilesWithCloud(path.join(projectPath, `block.json`), bs, true);
- } catch (err) {
- res.status(500).json({status: 500, message: err});
- }
-
- res.json({status: 200, message: 'Successfully synced!'});
-});
-
-// Errors handler
-app.use(handleSyntaxErrors);
-
-// 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')));
-
-// Custom Middleware
-app.use(setHeaders);
-
// Setup Gulp
-await buildAssetFiles();
-
-// BrowserSync
-shareUrl = await getShareableUrl();
-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 getDataOfFrame(isIframe = false) {
- const data = {config: getBlockConfigs({modulesPath, dataFiles})};
- if (data.error && data.errorMessage) {
- return data;
- }
-
- data.helpers = {
- include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath),
- }
-
- if (isIframe) {
- data.iframeMode = true;
- }
-
- return data;
-}
-
-function startBrowserSync() {
- return new Promise((resolve, reject) => {
- const listener = httpServer.listen(0, async () => {
- const PORT = listener.address().port;
-
- console.log(`The web server has started on port ${PORT}`);
-
- // BS is global variable.
- 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.posix.join(projectPath, "src/**/*.scss"), {delay: 400}, gulp.series(['build-styling-files', function (cb) {
- browserSyncReload(bs, 'css', 'Style Files Change');
- return cb();
- }]));
-
- bs.watch(path.join(projectPath, "src/**/*.hbs"), function () {
- return syncTemplate(sessions);
- });
-
- const args = {
- proxy: `http://localhost:${PORT}`,
- open: false
- };
-
- if (shareUrl) {
- args.socket = {
- domain: shareUrl
- };
- }
-
- bs.init(args, (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.posix.join(projectPath, "src/**/*.js"), path.posix.join(projectPath, "src/**/*.mjs"), "!" + path.posix.join(projectPath, "src/**/*.min.js")];
-}
-
-function buildScriptFiles(done) {
- const files = getJSBundleFiles();
- return 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')));
-}
-
-function buildStyleFiles(done) {
- return 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')))
-}
-
-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 () {
- 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();
-}
-
-function handleSyntaxErrors(err, req, res, next) {
- if (err) {
- return res.render('error', {
- helpers: {
- include_partial: (filesPath) => path.join(modulesPath, filesPath),
- },
- err
- });
- }
-
- next();
-}
-
-function handlebarLayoutsPath() {
- return path.join(...arguments)
- .replace(/\\/g, '/'); // Windows path issue. Fix all "\" for Handlebars.
-}
-
-function initSessionsServer(httpServer) {
- const io = new Server(httpServer);
- io.on('connection', async (socket) => {
- sessions.push(socket);
- await syncTemplate(sessions);
- });
-
- return httpServer;
-}
-
-function setHeaders(req, res, next) {
- res.setHeader('Access-Control-Allow-Origin', '*');
- next();
-}
-
-async function syncTemplate(sessions = []) {
- const blockName = config.has('blockName') ? config.get('blockName') : developmentBlockName;
- const hbsTemplate = await fs.readFile(handlebarLayoutsPath(projectPath, 'src', `${blockName}.template.hbs`), 'utf8');
-
- sessions.forEach(socket => {
- socket.emit('templateUpdate', {template: hbsTemplate});
- });
-}
-
-async function getShareableUrl() {
- let domain = PRODUCTION_REGISTRY_URL;
- let data = {}
-
- try {
- const response = await fetch(`${domain}/dev-tool/?devToolToken=${DevToolToken}`);
- data = await response.json();
- } catch (e) {
- console.log('Error:', `Can't load data from DevTool endpoint: ${domain}`);
- }
-
- if (data.statusCode !== 200) {
- console.log('Reply from DevTool endpoint:', data.message);
- return null;
- }
-
- let url;
- try {
- await authtoken(data.ngrokApiToken);
- url = await connect(port);
- } catch (e) {
- console.log('Error:', `Can't connect to ngrok, probably wrong token.`);
- }
-
- return url;
-}
-
-async function triggerGlobalProjectFilesBuild(project) {
- const response = await fetch(`${blocksRegistry}/project-files`, {
- method: 'POST',
- body: JSON.stringify({project}),
- headers: {'Content-Type': 'application/json'}
- });
-
- return response.json();
-}
-
-async function getDataFiles(projectPath) {
- const dataFiles = [];
-
- try {
- await fs.access(path.join(projectPath, 'data'));
-
- const files = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
- dataFiles.push(...files);
- } catch (e) {
- console.log('Warning: data folder not found.');
- }
- return dataFiles;
-}
+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);