You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

229 lines
5.8 KiB

#!/usr/bin/env node
import express from 'express';
import {create} from 'express-handlebars';
import fsExtra from 'fs-extra';
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";
/**
* Constants
*/
const isDev = process.env.NODE_ENV === 'development'; // Check README file in case you get "missing files" error.
const modulePath = isDev ? '' : 'node_modules/create-block-dev-tool/';
const projectDir = modulePath;
const sass = gulpSass(dartSass);
buildStyleFiles()
buildScriptFiles()
/**
* Init server
*/
let port = 3000;
const app = express();
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);
app.set('view engine', '.hbs');
app.set('views', projectDir + 'layouts');
const dataFiles = prepareListOfDataFiles(await fs.readdir('./data'));
app.get('/', async (req, res) => {
let jsonFileName = req.query.data ? req.query.data : 'default';
const data = await getBlockConfigs(jsonFileName);
if (data.error && data.errorMessage) {
return res.send(data.errorMessage);
}
data.helpers = {
port: port,
include_partial: (path) => projectDir + path,
baseView: config.has('baseView') ? config.get('baseView') : 'container',
}
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);
if (data.error && data.errorMessage) {
return res.send(data.errorMessage);
}
data.helpers = {
include_partial: (path) => projectDir + path,
include_block_template: (path) => 'src/' + (config.has('blockName') ? config.get('blockName') : 'development') + '.template',
}
const baseView = req.params.baseView ?? 'container';
res.render(baseView, data)
});
app.use(express.static('src'));
app.use(express.static(projectDir + 'layouts'));
const listener = app.listen(0, async () => {
const PORT = listener.address().port;
await open(`http://localhost:${PORT}`);
console.log(`The web server has started on port ${PORT}`);
const bs = browserSync.create();
gulp.watch(['src/**/*.js', 'src/**/*.mjs', '!src/**/*.min.js'], {delay: 400}, gulp.series([buildScriptFiles, function (cb) {
browserSyncReload(bs, 'js', 'Script Files Change');
return cb();
}]));
gulp.watch('src/**/*.scss', {delay: 400}, gulp.series([buildStyleFiles, function (cb) {
browserSyncReload(bs, 'css', 'Style Files Change');
return cb();
}]));
// bs.watch("src/**/*.js", function (event, file) {});
// bs.watch("src/**/*.css", function (event, file) {});
bs.watch("src/**/*.hbs", function (event, file) {
browserSyncReload(bs, '', 'Template File Change: ' + file)
});
bs.init({
proxy: `http://localhost:${PORT}`,
open: false
}, (err, bs) => {
port = bs.server._connectionKey.split(':').pop();
console.log(port)
});
});
/**
* Functions
*/
function browserSyncReload(bs, extension = '', message = '') {
if (isDev) {
// console.log(event, file);
console.log(message);
}
if (extension) {
extension = "*." + extension;
}
bs.reload(extension);
}
function buildScriptFiles() {
return gulp.src(['src/**/*.js', 'src/**/*.mjs', '!src/**/*.min.js'])
.pipe(sourcemaps.init({}))
.pipe(babel())
.pipe(gulp.src('vendor/*.js'))
// .pipe(gulp.dest('src/'))
.pipe(uglify())
.pipe(rename({extname: '.min.js'}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('src/'));
}
function buildStyleFiles() {
return gulp.src('src/**/*.scss')
.pipe(sourcemaps.init({}))
.pipe(sass.sync().on('error', sass.logError))
// .pipe(gulp.dest('src/'))
.pipe(rename({extname: '.min.css'}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('src/'))
}
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 getBlockConfigs(jsonFileName) {
if (!jsonFileName || !await fsExtra.exists(`./data/${jsonFileName}.json`)) {
jsonFileName = 'default';
}
let data = {};
try {
data = await fsExtra.readJson(`./data/${jsonFileName}.json`);
} catch (e) {
return {
error: true,
errorMessage: getErrorHtml("JSON Syntax error. Please make sure the dataFile is valid.", e),
};
}
Object.assign(data, {
config: Object.assign(
JSON.parse(JSON.stringify(config)), // The entire config object.
{
projectDir,
activeDataFile: jsonFileName,
dataFiles: dataFiles.map((name) => {
return {
name,
active: jsonFileName === name,
};
}),
remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
}
)
});
return data;
}
function getErrorHtml(message = '', errorMessage = '') {
return `<div style="padding: 10px 15px; font-family: Arial, sans-serif">
<p>${message}</p>
<pre style="padding: 10px 15px; background-color: #ffd0d0; border: 1px solid red;">${errorMessage}</pre>
</div>`;
}