@ -1,5 +1,6 @@
#!/usr/bin/env node
import path from 'path' ;
import fetch from "node-fetch" ;
import express from 'express' ;
import { create } from 'express-handlebars' ;
@ -19,29 +20,25 @@ 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' ;
/ * *
* Constants
* /
const isDev = process . env . NODE_ENV === 'development' || ( config . has ( 'isDev' ) && config . get ( 'isDev' ) ) ; // Check README file in case you get "missing files" error.
const { isDev , modulesPath , projectPath , developmentBlockName } = getConfigs ( ) ;
const blocksRegistry = isDev ? 'http://localhost:3020' : 'https://axe-web-blocks-registry.captain.devdevdev.life' ;
const modulePath = isDev ? '' : 'node_modules/block-dev-tool/' ;
const projectDir = modulePath ;
const sass = gulpSass ( dartSass ) ;
buildStyleFiles ( )
buildScriptFiles ( )
/ * *
* Init server
* /
let port = 3000 ; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
let previewFrameUrl = ` http://localhost: ${ port } ` ; // This variable is used in *.hbs and it will be updated once BrowserSync is ready.
const dataFiles = prepareListOfDataFiles ( await fs . readdir ( './ data') ) ;
let previewFrameUrl = ` http://localhost: ${ port } ` ; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
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 : {
@ -60,7 +57,7 @@ const hbs = create({
app . engine ( '.hbs' , hbs . engine ) ;
app . set ( 'view engine' , '.hbs' ) ;
app . set ( 'views' , projectDir + 'layouts' ) ;
app . set ( 'views' , path . join ( modulesPath , 'layouts' ) ) ;
//
// Routes
@ -68,7 +65,7 @@ app.set('views', projectDir + 'layouts');
app . get ( '/' , async ( req , res ) => {
let jsonFileName = req . query . data ? req . query . data : 'default' ;
const data = await getBlockConfigs ( jsonFileName , { includeConfigs : true } ) ;
const data = await getBlockConfigs ( jsonFileName , { includeConfigs : true , projectPath , modulesPath , dataFiles } ) ;
if ( data . error && data . errorMessage ) {
return res . send ( data . errorMessage ) ;
}
@ -78,7 +75,7 @@ app.get('/', async (req, res) => {
data . helpers = {
port ,
include_partial : ( path ) => projectDir + path ,
include_partial : ( filesPath ) => path . join ( modulesPath , filesPath ) ,
baseView ,
previewFrameUrl : ` ${ previewFrameUrl } / ${ baseViewUrl } ` ,
}
@ -88,16 +85,16 @@ app.get('/', async (req, res) => {
app . get ( '/view/:baseView' , async ( req , res ) => {
let jsonFileName = req . query . data ? req . query . data : 'default' ;
const data = await getBlockConfigs ( jsonFileName , { includeConfigs : true } ) ;
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' ) : 'development' ;
const blockName = config . has ( 'blockName' ) ? config . get ( 'blockName' ) : developmentBlockName ;
data . helpers = {
include_partial : ( path ) => projectDir + path ,
include_block_template : ( path ) => ` src/ ${ blockName } .template ` ,
include_partial : ( filesPath ) => path . join ( modulesPath , filesPath ) ,
include_block_template : ( ) => path . join ( projectPath , 'src' , ` ${ blockName } .template ` ) ,
section_class : ` ${ blockName } -- ${ jsonFileName } ` ,
base_url : '/'
}
@ -108,7 +105,7 @@ app.get('/view/:baseView', async (req, res) => {
} ) ;
app . get ( '/publish' , async ( req , res ) => {
const data = await readJSONFile ( './block.json' ) ;
const data = await readJSONFile ( path . join ( projectPath , ` block.json ` ) ) ;
let responseData ;
try {
@ -131,7 +128,7 @@ app.get('/publish', async (req, res) => {
if ( responseData . uploadUrl ) {
await zipProject ( ) ;
const body = await fs . readFile ( './ dist.zip') ;
const body = await fs . readFile ( path . join ( projectPath , 'dist.zip') ) ;
const response = await fetch ( ` ${ responseData . uploadUrl } ` , {
method : 'PUT' ,
body ,
@ -141,7 +138,7 @@ app.get('/publish', async (req, res) => {
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 ( './ dist.zip') ;
await fs . unlink ( path . join ( projectPath , 'dist.zip') ) ;
return ;
}
}
@ -149,15 +146,15 @@ app.get('/publish', async (req, res) => {
res . json ( { success : true } ) ;
await fs . unlink ( './ dist.zip') ;
await fs . unlink ( path . join ( projectPath , 'dist.zip') ) ;
} ) ;
app . get ( '/data' , async ( req , res ) => {
let jsonDataFileName = req . query . name ? req . query . name : 'default' ;
const data = await getBlockConfigs ( jsonDataFileName ) ;
const dataFiles = prepareListOfDataFiles ( await fs . readdir ( './data' ) ) ;
const designPreviewFiles = getListOfDesignPreviewFiles ( jsonDataFileName , await fs . readdir ( './design/preview' ) ) ;
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 ,
@ -170,9 +167,12 @@ app.get('/data', async (req, res) => {
app . use ( handleSyntaxErrors ) ;
// Static Files
app . use ( express . static ( 'src' ) ) ;
app . use ( express . static ( 'design' ) ) ;
app . use ( express . static ( projectDir + 'layouts' ) ) ;
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 ( ) ;
@ -212,12 +212,13 @@ function startBrowserSync() {
const bs = browserSync . create ( ) ;
gulp . watch ( [ 'src/**/*.js' , 'src/**/*.mjs' , '!src/**/*.min.js' ] , { delay : 400 } , gulp . series ( [ buildScriptFiles , function ( cb ) {
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 ( 'src/**/*.scss', { delay : 400 } , gulp . series ( [ buildStyleFiles , function ( 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 ( ) ;
} ] ) ) ;
@ -259,26 +260,58 @@ function browserSyncReload(bs, extension = '', message = '') {
bs . reload ( extension ) ;
}
function buildScriptFiles ( ) {
return gulp . src ( [ 'src/**/*.js' , 'src/**/*.mjs' , '!src/**/*.min.js' ] )
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 ( ) )
. pipe ( gulp . src ( 'vendor/*.js' ) )
. 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 ( 'src/') ) ;
. pipe ( gulp . dest ( path . join ( projectPath , 'src/') ) ) ;
}
function buildStyleFiles ( ) {
return gulp . src ( 'src/**/*.scss')
function buildStyleFiles ( done ) {
return gulp . src ( path . join ( projectPath , 'src/**/*.scss') )
. pipe ( sourcemaps . init ( { } ) )
. pipe ( sass . sync ( ) . on ( 'error' , sass . logError ) )
. 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 ( 'src/' ) )
. 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 ) {
@ -292,48 +325,6 @@ function prepareListOfDataFiles(dataFiles) {
. sort ( ) ;
}
async function readJSONFile ( jsonFile ) {
let data = { } ;
try {
data = await fsExtra . readJson ( jsonFile ) ;
} catch ( e ) {
return {
error : true , errorMessage : getErrorHtml ( "JSON Syntax error. Please make sure the dataFile is valid." , e ) ,
} ;
}
return data ;
}
async function getBlockConfigs ( jsonFileName = 'default' , { includeConfigs } = { } ) {
let data = await readJSONFile ( ` ./data/ ${ jsonFileName } .json ` ) ;
if ( data . error ) {
return data ;
}
if ( includeConfigs ) {
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 } < / p r e >
< / d i v > ` ;
}
async function zipProject ( ) {
// create a file to stream archive data to.
@ -372,8 +363,8 @@ async function zipProject() {
// pipe archive data to the file
archive . pipe ( output ) ;
// append files from a sub- directory, putting its contents at the root of archive
archive . directory ( 'src/ ', false ) ;
// 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
@ -384,7 +375,7 @@ function handleSyntaxErrors(err, req, res, next) {
if ( err ) {
return res . render ( 'error' , {
helpers : {
include_partial : ( path ) => projectDir + path ,
include_partial : ( filesPath ) => path . join ( modulesPath , filesPath ) ,
} ,
err
} ) ;
@ -392,4 +383,3 @@ function handleSyntaxErrors(err, req, res, next) {
next ( ) ;
}